summaryrefslogtreecommitdiff
path: root/Src
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
parent841bce705a58b04220b1f257abcc00ae71cbdbdc (diff)
parent001cba48ce3b964cf01fb3e2af54b20eacbc9bf5 (diff)
downloadzsh-26e09889646be3ea65b4a3dfeda26213e4bb6a27.tar.gz
zsh-26e09889646be3ea65b4a3dfeda26213e4bb6a27.zip
Merge branch 'upstream' into debian
Diffstat (limited to 'Src')
-rw-r--r--Src/Builtins/rlimits.c8
-rw-r--r--Src/Makemod.in.in23
-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
-rw-r--r--Src/Zle/compcore.c43
-rw-r--r--Src/Zle/compctl.c4
-rw-r--r--Src/Zle/complete.c6
-rw-r--r--Src/Zle/complist.c84
-rw-r--r--Src/Zle/compmatch.c14
-rw-r--r--Src/Zle/compresult.c22
-rw-r--r--Src/Zle/computil.c19
-rw-r--r--Src/Zle/textobjects.c6
-rw-r--r--Src/Zle/zle.h17
-rw-r--r--Src/Zle/zle_hist.c33
-rw-r--r--Src/Zle/zle_keymap.c85
-rw-r--r--Src/Zle/zle_main.c33
-rw-r--r--Src/Zle/zle_misc.c3
-rw-r--r--Src/Zle/zle_refresh.c500
-rw-r--r--Src/Zle/zle_thingy.c6
-rw-r--r--Src/Zle/zle_tricky.c71
-rw-r--r--Src/Zle/zle_utils.c55
-rw-r--r--Src/Zle/zle_vi.c9
-rw-r--r--Src/builtin.c383
-rw-r--r--Src/compat.c112
-rw-r--r--Src/exec.c312
-rw-r--r--Src/glob.c17
-rw-r--r--Src/hashtable.c28
-rw-r--r--Src/hist.c93
-rw-r--r--Src/init.c261
-rw-r--r--Src/input.c57
-rw-r--r--Src/jobs.c349
-rw-r--r--Src/lex.c126
-rw-r--r--Src/loop.c44
-rw-r--r--Src/makepro.awk6
-rw-r--r--Src/math.c44
-rw-r--r--Src/mem.c8
-rw-r--r--Src/mkbltnmlst.sh36
-rw-r--r--Src/modentry.c10
-rw-r--r--Src/module.c34
-rw-r--r--Src/options.c35
-rw-r--r--Src/params.c580
-rw-r--r--Src/parse.c41
-rw-r--r--Src/pattern.c104
-rw-r--r--Src/prompt.c429
-rw-r--r--Src/prototypes.h54
-rw-r--r--Src/signals.c322
-rw-r--r--Src/signals.h34
-rw-r--r--Src/signames2.awk18
-rw-r--r--Src/sort.c4
-rw-r--r--Src/string.c15
-rw-r--r--Src/subst.c358
-rw-r--r--Src/text.c14
-rw-r--r--Src/utils.c471
-rw-r--r--Src/zsh.h232
-rw-r--r--Src/zsh_system.h23
-rw-r--r--Src/ztype.h3
77 files changed, 5334 insertions, 2333 deletions
diff --git a/Src/Builtins/rlimits.c b/Src/Builtins/rlimits.c
index 5f9c84b0f..65226dc9a 100644
--- a/Src/Builtins/rlimits.c
+++ b/Src/Builtins/rlimits.c
@@ -145,6 +145,10 @@ static const resinfo_T known_resources[] = {
{RLIMIT_UMTXP, "umtxp", ZLIMTYPE_NUMBER, 1,
'o', "umtx shared locks"},
# endif
+# ifdef HAVE_RLIMIT_PIPEBUF /* FreeBSD */
+ {RLIMIT_PIPEBUF, "pipebuf", ZLIMTYPE_MEMORY, 1024,
+ 'y', "size of buffers for pipes/fifos"},
+#endif
# ifdef HAVE_RLIMIT_POSIXLOCKS /* DragonFly */
{RLIMIT_POSIXLOCKS, "posixlocks", ZLIMTYPE_NUMBER, 1,
@@ -171,6 +175,10 @@ static const resinfo_T known_resources[] = {
{RLIMIT_TCACHE, "cachedthreads", ZLIMTYPE_NUMBER, 1,
'N', "cached threads"},
# endif
+# ifdef HAVE_RLIMIT_NOVMON /* Haiku */
+ {RLIMIT_NOVMON, "vnodemonitors", ZLIMTYPE_NUMBER, 1,
+ 'N', "open vnode monitors"},
+# endif
};
/* resinfo[RLIMIT_XXX] points to the corresponding entry
diff --git a/Src/Makemod.in.in b/Src/Makemod.in.in
index ea0cdc3a4..3343ae1d0 100644
--- a/Src/Makemod.in.in
+++ b/Src/Makemod.in.in
@@ -52,32 +52,17 @@ DLCOMPILE = $(CC) -c -I. -I$(dir_top)/Src -I$(sdir_top)/Src -I$(sdir_top)/Src/
LINK = $(CC) $(LDFLAGS) $(EXELDFLAGS) $(EXTRA_LDFLAGS) -o $@
DLLINK = $(DLLD) $(LDFLAGS) $(LIBLDFLAGS) $(DLLDFLAGS) -o $@
-KNR_OBJ=.o
-KNROBJ=._foo_
+OBJ=.o
-ANSIOBJ=.o
-ANSI_OBJ=._foo_
+.SUFFIXES: .c .$(DL_EXT) ..o .o .syms .pro .epro
-.SUFFIXES: .c .$(DL_EXT) ..o .._foo_ .o ._foo_ .syms .pro .epro
-
-.c$(ANSI@U@OBJ):
+.c$(OBJ):
$(COMPILE) -o $@ $<
@rm -f $(dir_src)/stamp-modobjs
-.c$(KNR@U@OBJ):
- @ANSI2KNR@ $< > $@.c
- $(COMPILE) -o $@ $@.c
- rm -f $@.c
- @rm -f $(dir_src)/stamp-modobjs
-
-.c.$(ANSI@U@OBJ):
+.c.$(OBJ):
$(DLCOMPILE) -o $@ $<
-.c.$(KNR@U@OBJ):
- @ANSI2KNR@ $< > $@.c
- $(DLCOMPILE) -o $@ $@.c
- rm -f $@.c
-
.c.syms:
$(AWK) -f $(sdir_src)/makepro.awk $< $(subdir) > $@
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;
diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c
index 4ac5d089f..fb703f801 100644
--- a/Src/Zle/compcore.c
+++ b/Src/Zle/compcore.c
@@ -708,7 +708,7 @@ callcompfunc(char *s, char *fn)
sav = *ss;
*ss = '\0';
- tmp = (linwhat == IN_MATH ? dupstring(s) : multiquote(s, 0));
+ tmp = (linwhat == IN_MATH ? dupstring_wlen(s, offs) : multiquote(s, 0));
untokenize(tmp);
compprefix = ztrdup(tmp);
*ss = sav;
@@ -1230,14 +1230,14 @@ check_param(char *s, int set, int test)
else if (idigit(*e))
while (idigit(*e))
e++;
- else if ((ie = itype_end(e, IIDENT, 0)) != e) {
+ else if ((ie = itype_end(e, INAMESPC, 0)) != e) {
do {
e = ie;
if (comppatmatch && *comppatmatch &&
(*e == Star || *e == Quest))
ie = e + 1;
else
- ie = itype_end(e, IIDENT, 0);
+ ie = itype_end(e, INAMESPC, 0);
} while (ie != e);
}
@@ -1820,7 +1820,7 @@ set_comp_sep(void)
*/
sav = s[(i = swb - 1 - sqq + dq)];
s[i] = '\0';
- qp = (qttype == QT_SINGLE) ? dupstring(s) : rembslash(s);
+ qp = (qttype == QT_SINGLE) ? dupstring_wlen(s, i) : rembslash(s);
s[i] = sav;
if (swe < swb)
swe = swb;
@@ -2244,13 +2244,14 @@ addmatches(Cadata dat, char **argv)
if (dat->aflags & CAF_MATCH) {
lipre = dupstring(compiprefix);
lisuf = dupstring(compisuffix);
- lpre = dupstring(compprefix);
- lsuf = dupstring(compsuffix);
- llpl = strlen(lpre);
- llsl = strlen(lsuf);
-
- if (llpl + (int)strlen(compqiprefix) + (int)strlen(lipre) != origlpre
- || llsl + (int)strlen(compqisuffix) + (int)strlen(lisuf) != origlsuf)
+ llpl = strlen(compprefix);
+ llsl = strlen(compsuffix);
+ lpre = dupstring_wlen(compprefix, llpl);
+ lsuf = dupstring_wlen(compsuffix, llsl);
+
+ /* This used to reference compqiprefix and compqisuffix, why? */
+ if (llpl + (int)strlen(qipre) + (int)strlen(lipre) != origlpre
+ || llsl + (int)strlen(qisuf) + (int)strlen(lisuf) != origlsuf)
lenchanged = 1;
/* Test if there is an existing -P prefix. */
@@ -2299,12 +2300,8 @@ addmatches(Cadata dat, char **argv)
for (p = lpre + 2; *p && *p != ')'; p++);
if (*p == ')') {
- char sav = p[1];
-
- p[1] = '\0';
- globflag = dupstring(lpre);
gfl = p - lpre + 1;
- p[1] = sav;
+ globflag = dupstring_wlen(lpre, gfl);
lpre = p + 1;
llpl -= gfl;
@@ -2730,7 +2727,7 @@ add_match_data(int alt, char *str, char *orig, Cline line,
sl = tsl;
}
if (qisl) {
- Cline qsl = bld_parts(dupstring(qisuf), qisl, qisl, NULL, NULL);
+ Cline qsl = bld_parts(dupstring_wlen(qisuf, qisl), qisl, qisl, NULL, NULL);
qsl->flags |= CLF_SUF;
qsl->suffix = qsl->prefix;
@@ -2813,7 +2810,7 @@ add_match_data(int alt, char *str, char *orig, Cline line,
line = p;
}
if (qipl) {
- Cline lp, p = bld_parts(dupstring(qipre), qipl, qipl, &lp, NULL);
+ Cline lp, p = bld_parts(dupstring_wlen(qipre, qipl), qipl, qipl, &lp, NULL);
lp->next = line;
line = p;
@@ -2898,9 +2895,9 @@ add_match_data(int alt, char *str, char *orig, Cline line,
*t++ = '$';
*t++ = '\'';
*t++ = '\\';
- *t++ = '0' + ((STOUC(curchar) >> 6) & 7);
- *t++ = '0' + ((STOUC(curchar) >> 3) & 7);
- *t++ = '0' + (STOUC(curchar) & 7);
+ *t++ = '0' + (((unsigned char) curchar >> 6) & 7);
+ *t++ = '0' + (((unsigned char) curchar >> 3) & 7);
+ *t++ = '0' + ((unsigned char) curchar & 7);
*t++ = '\'';
} while (cnt == MB_INCOMPLETE && fs < fe);
/* Scanning restarts from the spot after the char we skipped. */
@@ -3252,7 +3249,7 @@ makearray(LinkList l, int type, int flags, int *np, int *nlp, int *llp)
/* Now sort the array (it contains matches). */
matchorder = flags;
qsort((void *) rp, n, sizeof(Cmatch),
- (int (*) _((const void *, const void *)))matchcmp);
+ (int (*) (const void *, const void *))matchcmp);
/* since the matches are sorted and the default is to remove
* all duplicates, -1 (remove only consecutive dupes) is a no-op,
@@ -3294,7 +3291,7 @@ makearray(LinkList l, int type, int flags, int *np, int *nlp, int *llp)
sp = (Cmatch *) zhalloc((n + 1) * sizeof(Cmatch));
memcpy(sp, rp, (n + 1) * sizeof(Cmatch));
qsort((void *) sp, n, sizeof(Cmatch),
- (int (*) _((const void *, const void *)))matchcmp);
+ (int (*) (const void *, const void *))matchcmp);
for (asp = sp + 1; *asp; asp++) {
Cmatch *ap = asp - 1, *bp = asp;
if (matcheq(*ap, *bp)) {
diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c
index 08355d1b9..de3ccdfce 100644
--- a/Src/Zle/compctl.c
+++ b/Src/Zle/compctl.c
@@ -3201,8 +3201,8 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
memcpy(lpre, s, lpl);
lpre[lpl] = '\0';
qlpre = quotename(lpre);
- lsuf = dupstring(s + offs);
- lsl = strlen(lsuf);
+ lsl = strlen(s + offs);
+ lsuf = dupstring_wlen(s + offs, lsl);
qlsuf = quotename(lsuf);
/* First check for ~.../... */
diff --git a/Src/Zle/complete.c b/Src/Zle/complete.c
index 67a60963e..342611f1f 100644
--- a/Src/Zle/complete.c
+++ b/Src/Zle/complete.c
@@ -518,7 +518,7 @@ parse_class(Cpattern p, char *iptr)
ch = range_type((char *)iptr, nptr-iptr);
iptr = nptr + 2;
if (ch != PP_UNKWN)
- *optr++ = STOUC(Meta) + ch;
+ *optr++ = (unsigned char) Meta + ch;
} else {
/* characters stay metafied */
char *ptr1 = iptr;
@@ -829,7 +829,9 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
ca_args:
- if (mstr && (match = parse_cmatcher(name, mstr)) == pcm_err) {
+ if (mstr && (dat.aflags & CAF_MATCH) &&
+ (match = parse_cmatcher(name, mstr)) == pcm_err)
+ {
zsfree(mstr);
zfree(dat.dpar, dparsize);
return 1;
diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c
index 0dc64db6a..091ad03b1 100644
--- a/Src/Zle/complist.c
+++ b/Src/Zle/complist.c
@@ -291,12 +291,12 @@ getcolval(char *s, int multi)
case '?': *p = '\177'; break;
default:
if (*s >= '0' && *s <= '7') {
- int i = STOUC(*s);
+ int i = (unsigned char) *s;
if (*++s >= '0' && *s <= '7') {
- i = (i * 8) + STOUC(*s);
+ i = (i * 8) + (unsigned char) *s;
if (*++s >= '0' && *s <= '7')
- i = (i * 8) + STOUC(*s);
+ i = (i * 8) + (unsigned char) *s;
}
*p = (char) i;
} else
@@ -305,7 +305,7 @@ getcolval(char *s, int multi)
} else if (*s == '^') {
if ((s[1] >= '@' && s[1] <= '_') ||
(s[1] >= 'a' && s[1] <= 'z'))
- *p = (char) (STOUC(*s) & ~0x60);
+ *p = (char) ((unsigned char) *s & ~0x60);
else if (s[1] == '?')
*p = '\177';
else {
@@ -794,7 +794,7 @@ clnicezputs(int do_colors, char *s, int ml)
*/
for (t = sptr; *t; t++) {
/* Input is metafied... */
- int nc = (*t == Meta) ? STOUC(*++t ^ 32) : STOUC(*t);
+ int nc = (*t == Meta) ? (unsigned char) (*++t ^ 32) : (unsigned char) *t;
/* Is the screen full? */
if (ml == mlend - 1 && col == zterm_columns - 1) {
mlprinted = ml - oml;
@@ -852,7 +852,7 @@ clnicezputs(int do_colors, char *s, int ml)
cc = *s++ ^ 32;
for (t = nicechar(cc); *t; t++) {
- int nc = (*t == Meta) ? STOUC(*++t ^ 32) : STOUC(*t);
+ int nc = (*t == Meta) ? (unsigned char) (*++t ^ 32) : (unsigned char) *t;
if (ml == mlend - 1 && col == zterm_columns - 1) {
mlprinted = ml - oml;
return 0;
@@ -1072,7 +1072,7 @@ static int
compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop)
{
char *p, nc[2*DIGBUFSIZE + 12], nbuf[2*DIGBUFSIZE + 12];
- int l = 0, cc = 0, b = 0, s = 0, u = 0, m, ask, beg, stat;
+ int l = 0, cc = 0, m, ask, beg, stat;
if ((stat = !fmt)) {
if (mlbeg >= 0) {
@@ -1118,48 +1118,46 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop)
m = 0;
switch (cchar) {
case ZWC('%'):
- if (dopr == 1)
+ if (dopr == 1) {
+ applytextattributes(0);
putc('%', shout);
+ }
cc++;
break;
case ZWC('n'):
if (!stat) {
sprintf(nc, "%d", n);
- if (dopr == 1)
+ if (dopr == 1) {
+ applytextattributes(0);
fputs(nc, shout);
+ }
/* everything here is ASCII... */
cc += strlen(nc);
}
break;
case ZWC('B'):
- b = 1;
if (dopr)
- tcout(TCBOLDFACEBEG);
+ tsetattrs(TXTBOLDFACE);
break;
case ZWC('b'):
- b = 0; m = 1;
if (dopr)
- tcout(TCALLATTRSOFF);
+ tunsetattrs(TXTBOLDFACE);
break;
case ZWC('S'):
- s = 1;
if (dopr)
- tcout(TCSTANDOUTBEG);
+ tsetattrs(TXTSTANDOUT);
break;
case ZWC('s'):
- s = 0; m = 1;
if (dopr)
- tcout(TCSTANDOUTEND);
+ tunsetattrs(TXTSTANDOUT);
break;
case ZWC('U'):
- u = 1;
if (dopr)
- tcout(TCUNDERLINEBEG);
+ tsetattrs(TXTUNDERLINE);
break;
case ZWC('u'):
- u = 0; m = 1;
if (dopr)
- tcout(TCUNDERLINEEND);
+ tunsetattrs(TXTUNDERLINE);
break;
case ZWC('F'):
case ZWC('K'):
@@ -1173,20 +1171,28 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop)
} else
atr = match_colour(NULL, is_fg, arg);
if (atr != TXT_ERROR && dopr)
- set_colour_attribute(atr, is_fg ? COL_SEQ_FG :
- COL_SEQ_BG, 0);
+ tsetattrs(atr);
break;
case ZWC('f'):
if (dopr)
- set_colour_attribute(TXTNOFGCOLOUR, COL_SEQ_FG, 0);
+ tunsetattrs(TXTFGCOLOUR);
break;
case ZWC('k'):
if (dopr)
- set_colour_attribute(TXTNOBGCOLOUR, COL_SEQ_BG, 0);
+ tunsetattrs(TXTBGCOLOUR);
+ break;
+ case ZWC('H'):
+ if (*p == '{') {
+ p = parsehighlight(p + 1, '}', &atr);
+ if (atr != TXT_ERROR && dopr)
+ treplaceattrs(atr);
+ }
break;
case ZWC('{'):
if (arg)
cc += arg;
+ if (dopr)
+ applytextattributes(0);
for (; *p && (*p != '%' || p[1] != '}'); p++)
if (dopr)
putc(*p == Meta ? *++p ^ 32 : *p, shout);
@@ -1197,7 +1203,7 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop)
if (stat) {
sprintf(nc, "%d/%d", (n ? mlastm : mselect),
listdat.nlist);
- m = 2;
+ m = 1;
}
break;
case ZWC('M'):
@@ -1205,20 +1211,20 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop)
sprintf(nbuf, "%d/%d", (n ? mlastm : mselect),
listdat.nlist);
sprintf(nc, "%-9s", nbuf);
- m = 2;
+ m = 1;
}
break;
case ZWC('l'):
if (stat) {
sprintf(nc, "%d/%d", ml + 1, listdat.nlines);
- m = 2;
+ m = 1;
}
break;
case ZWC('L'):
if (stat) {
sprintf(nbuf, "%d/%d", ml + 1, listdat.nlines);
sprintf(nc, "%-9s", nbuf);
- m = 2;
+ m = 1;
}
break;
case ZWC('p'):
@@ -1230,7 +1236,7 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop)
((ml + 1) * 100) / listdat.nlines);
else
strcpy(nc, "Top");
- m = 2;
+ m = 1;
}
break;
case ZWC('P'):
@@ -1242,25 +1248,19 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop)
((ml + 1) * 100) / listdat.nlines);
else
strcpy(nc, "Top ");
- m = 2;
+ m = 1;
}
break;
}
- if (m == 2 && dopr == 1) {
+ if (m && dopr) {
/* nc only contains ASCII text */
int l = strlen(nc);
if (l + cc > zterm_columns - 2)
nc[l -= l + cc - (zterm_columns - 2)] = '\0';
+ applytextattributes(0);
fputs(nc, shout);
cc += l;
- } else if (dopr && m == 1) {
- if (b)
- tcout(TCBOLDFACEBEG);
- if (s)
- tcout(TCSTANDOUTBEG);
- if (u)
- tcout(TCUNDERLINEBEG);
}
} else
break;
@@ -1276,6 +1276,7 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop)
cc = 0;
}
if (dopr == 1) {
+ applytextattributes(0);
if (ml == mlend - 1 && (cc % zterm_columns) ==
zterm_columns - 1) {
dopr = 0;
@@ -1311,6 +1312,8 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop)
}
}
if (dopr) {
+ treplaceattrs(0);
+ applytextattributes(0);
if (!(cc % zterm_columns))
fputs(" \010", shout);
cleareol();
@@ -2388,6 +2391,9 @@ domenuselect(Hookdef dummy, Chdata dat)
char *s;
char status[MAX_STATUS], *modeline = NULL;
+ if (! hasoldlist)
+ return 2;
+
msearchstack = NULL;
msearchstr = "";
msearchstate = MS_OK;
diff --git a/Src/Zle/compmatch.c b/Src/Zle/compmatch.c
index bb8359f1d..b58bd1f05 100644
--- a/Src/Zle/compmatch.c
+++ b/Src/Zle/compmatch.c
@@ -1161,8 +1161,8 @@ comp_match(char *pfx, char *sfx, char *w, Patprog cp, Cline *clp, int qu,
/* We still break it into parts here, trying to build a sensible
* cline list for these matches, too. */
- w = dupstring(w);
wl = strlen(w);
+ w = dupstring_wlen(w, wl);
*clp = bld_parts(w, wl, wl, NULL, NULL);
*exact = 0;
} else {
@@ -1319,7 +1319,7 @@ pattern_match_equivalence(Cpattern lp, convchar_t wind, int wmtp,
convchar_t lchr;
int lmtp;
- if (!PATMATCHINDEX(lp->u.str, wind, &lchr, &lmtp)) {
+ if (!PATMATCHINDEX(lp->u.str, wind-1, &lchr, &lmtp)) {
/*
* No equivalent. No possible match; give up.
*/
@@ -2045,12 +2045,12 @@ join_strs(int la, char *sa, int lb, char *sb)
zlelineasstring(line, mp->llen, 0, &convlen,
NULL, 0);
if (rr <= convlen) {
- char *or = rs;
+ ptrdiff_t diff = rp - rs;
int alloclen = (convlen > 20) ? convlen : 20;
rs = realloc(rs, (rl += alloclen));
rr += alloclen;
- rp += rs - or;
+ rp = rs + diff;
}
memcpy(rp, convstr, convlen);
rp += convlen;
@@ -2073,11 +2073,11 @@ join_strs(int la, char *sa, int lb, char *sb)
} else {
/* Same character, just take it. */
if (rr <= 1 /* HERE charlen */) {
- char *or = rs;
+ ptrdiff_t diff = rp - rs;
rs = realloc(rs, (rl += 20));
rr += 20;
- rp += rs - or;
+ rp = rs + diff;
}
/* HERE: multibyte char */
*rp++ = *sa;
@@ -2127,7 +2127,7 @@ cmp_anchors(Cline o, Cline n, int join)
(j = join_strs(o->wlen, o->word, n->wlen, n->word))) {
o->flags |= CLF_JOIN;
o->wlen = strlen(j);
- o->word = dupstring(j);
+ o->word = dupstring_wlen(j, o->wlen);
return 2;
}
diff --git a/Src/Zle/compresult.c b/Src/Zle/compresult.c
index 57789c0f3..7dbc5676a 100644
--- a/Src/Zle/compresult.c
+++ b/Src/Zle/compresult.c
@@ -489,7 +489,7 @@ static char *
build_pos_string(LinkList list)
{
LinkNode node;
- int l;
+ int l, buflen;
char buf[40], *s;
long p;
@@ -499,12 +499,12 @@ build_pos_string(LinkList list)
/* This could be used to put an extra colon before the end-of-word
* position if there is nothing missing. */
if (p < 0)
- sprintf(buf, ":%ld", -p);
+ buflen = sprintf(buf, ":%ld", -p);
else
#endif
- sprintf(buf, "%ld", p);
- setdata(node, dupstring(buf));
- l += 1 + strlen(buf);
+ buflen = sprintf(buf, "%ld", p);
+ setdata(node, dupstring_wlen(buf, buflen));
+ l += 1 + buflen;
}
s = (char *) zalloc(l * sizeof(char));
*s = 0;
@@ -897,7 +897,7 @@ void
do_allmatches(UNUSED(int end))
{
int first = 1, nm = nmatches - 1, omc = menucmp, oma = menuacc, e;
- Cmatch *mc;
+ Cmatch *mc = 0;
struct menuinfo mi;
char *p = (brbeg ? ztrdup(lastbrbeg->str) : NULL);
@@ -915,10 +915,10 @@ do_allmatches(UNUSED(int end))
#endif
}
+ if (minfo.group)
+ mc = (minfo.group)->matches;
- mc = (minfo.group)->matches;
-
- while (1) {
+ while (mc) {
if (!((*mc)->flags & CMF_ALL)) {
if (!first)
accept_last();
@@ -1731,8 +1731,6 @@ calclist(int showall)
width < zterm_columns && nth < g->dcount;
nth++, tcol++) {
- m = *p;
-
if (tcol == tcols) {
tcol = 0;
tlines++;
@@ -1994,7 +1992,6 @@ printlist(int over, CLPrintFunc printm, int showall)
(listdat.onlyexpl & ((*e)->always > 0 ? 2 : 1)))) {
if (pnl) {
putc('\n', shout);
- pnl = 0;
ml++;
if (cl >= 0 && --cl <= 1) {
cl = -1;
@@ -2087,7 +2084,6 @@ printlist(int over, CLPrintFunc printm, int showall)
(showall || !(m->flags & (CMF_HIDE|CMF_NOLIST)))) {
if (pnl) {
putc('\n', shout);
- pnl = 0;
ml++;
if (cl >= 0 && --cl <= 1) {
cl = -1;
diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c
index 59abb4cc4..6ac458c91 100644
--- a/Src/Zle/computil.c
+++ b/Src/Zle/computil.c
@@ -911,7 +911,7 @@ struct cadef {
Caarg rest; /* the rest-argument */
char **defs; /* the original strings */
int ndefs; /* number of ... */
- int lastt; /* last time this was used */
+ time_t lastt; /* last time this was used */
Caopt *single; /* array of single-letter options */
char *match; /* -M spec to use */
int argsactive; /* if normal arguments are still allowed */
@@ -1296,8 +1296,7 @@ parse_cadef(char *nam, char **args)
int l = strlen(p) - 1;
if (*p == '(' && p[l] == ')') {
- axor = p = dupstring(p + 1);
- p[l - 1] = '\0';
+ axor = p = dupstring_wlen(p + 1, l - 1);
} else
axor = NULL;
if (!*p) {
@@ -1317,8 +1316,7 @@ parse_cadef(char *nam, char **args)
p = *++args;
l = strlen(p) - 1;
if (*p == '(' && p[l] == ')') {
- axor = p = dupstring(p + 1);
- p[l - 1] = '\0';
+ axor = p = dupstring_wlen(p + 1, l - 1);
} else
axor = NULL;
if (!*p) {
@@ -1339,7 +1337,7 @@ parse_cadef(char *nam, char **args)
LinkList list = newlinklist();
LinkNode node;
- char **xp, sav;
+ char **xp;
while (*p && *p != ')') {
for (p++; inblank(*p); p++);
@@ -1351,11 +1349,8 @@ parse_cadef(char *nam, char **args)
if (!*p)
break;
- sav = *p;
- *p = '\0';
- addlinknode(list, dupstring(q));
+ addlinknode(list, dupstring_wlen(q, p - q));
xnum++;
- *p = sav;
}
/* Oops, end-of-string. */
if (*p != ')') {
@@ -2935,7 +2930,7 @@ struct cvdef {
Cvval vals; /* value definitions */
char **defs; /* original strings */
int ndefs; /* number of ... */
- int lastt; /* last time used */
+ time_t lastt; /* last time used */
int words; /* if to look at other words */
};
@@ -4383,7 +4378,7 @@ cfp_matcher_range(Cmatcher *ms, char *add)
* word pattern.
*/
if ((ind = pattern_match_equivalence
- (m->word, ind, mt, addc)) != CHR_INVALID) {
+ (m->word, ind+1, mt, addc)) != CHR_INVALID) {
if (ret) {
if (imeta(ind)) {
*p++ = Meta;
diff --git a/Src/Zle/textobjects.c b/Src/Zle/textobjects.c
index c93777b65..a68c5296e 100644
--- a/Src/Zle/textobjects.c
+++ b/Src/Zle/textobjects.c
@@ -283,9 +283,9 @@ selectargument(UNUSED(char **args))
free(linein);
if (IS_THINGY(bindk, selectinshellword)) {
- ZLE_CHAR_T *match = ZWS("`\'\"");
- ZLE_CHAR_T *lmatch = ZWS("\'({"), *rmatch = ZWS("\')}");
- ZLE_CHAR_T *ematch = match, *found;
+ const ZLE_CHAR_T *match = ZWS("`\'\"");
+ const ZLE_CHAR_T *lmatch = ZWS("\'({"), *rmatch = ZWS("\')}");
+ const ZLE_CHAR_T *ematch = match, *found;
int start, end = zlecs;
/* for 'in' widget, don't include initial blanks ... */
while (mark < zlecs && ZC_iblank(zleline[mark]))
diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h
index 391586c4a..5bb9e7a5e 100644
--- a/Src/Zle/zle.h
+++ b/Src/Zle/zle.h
@@ -186,7 +186,7 @@ typedef struct thingy *Thingy;
/* widgets (ZLE functions) */
-typedef int (*ZleIntFunc) _((char **));
+typedef int (*ZleIntFunc) (char **);
struct widget {
int flags; /* flags (see below) */
@@ -258,6 +258,9 @@ struct modifier {
#define MOD_NULL (1<<5) /* throw away text for the vi cut buffer */
#define MOD_CHAR (1<<6) /* force character-wise movement */
#define MOD_LINE (1<<7) /* force line-wise movement */
+#define MOD_PRI (1<<8) /* OS primary selection for the vi cut buffer */
+#define MOD_CLIP (1<<9) /* OS clipboard for the vi cut buffer */
+#define MOD_OSSEL (MOD_PRI | MOD_CLIP) /* either system selection */
/* current modifier status */
@@ -316,7 +319,7 @@ struct vichange {
typedef struct keymap *Keymap;
-typedef void (*KeyScanFunc) _((char *, Thingy, char *, void *));
+typedef void (*KeyScanFunc) (char *, Thingy, char *, void *);
#define invicmdmode() (!strcmp(curkeymapname, "vicmd"))
@@ -432,6 +435,8 @@ enum {
struct region_highlight {
/* Attributes turned on in the region */
zattr atr;
+ /* Priority for this region relative to others that overlap */
+ int layer;
/* Start of the region */
int start;
/* Start of the region in metafied ZLE line */
@@ -487,11 +492,7 @@ typedef struct {
*/
REFRESH_CHAR chr;
/*
- * Its attributes. 'On' attributes (TXT_ATTR_ON_MASK) are
- * applied before the character, 'off' attributes (TXT_ATTR_OFF_MASK)
- * after it. 'On' attributes are present for all characters that
- * need the effect; 'off' attributes are only present for the
- * last character in the sequence.
+ * Its attributes.
*/
zattr atr;
} REFRESH_ELEMENT;
@@ -523,7 +524,7 @@ typedef REFRESH_ELEMENT *REFRESH_STRING;
((int)((unsigned)(x) - ZSH_INVALID_WCHAR_BASE))
/* Turn a single byte character into a private wide character */
#define ZSH_CHAR_TO_INVALID_WCHAR(x) \
- ((wchar_t)(STOUC(x) + ZSH_INVALID_WCHAR_BASE))
+ ((wchar_t)((unsigned char) x + ZSH_INVALID_WCHAR_BASE))
#endif
diff --git a/Src/Zle/zle_hist.c b/Src/Zle/zle_hist.c
index cfaa70dae..53c722621 100644
--- a/Src/Zle/zle_hist.c
+++ b/Src/Zle/zle_hist.c
@@ -68,6 +68,13 @@ Keymap isearch_keymap;
*/
#define GETZLETEXT(ent) ((ent)->zle_text ? (ent)->zle_text : (ent)->node.nam)
+/*
+ * Flag that edits have been made to a zle line.
+ * If not set, nothing to forget.
+ */
+/**/
+int have_edits = 0;
+
/**/
void
remember_edits(void)
@@ -81,6 +88,7 @@ remember_edits(void)
if (ent->zle_text)
free(ent->zle_text);
ent->zle_text = zlemetaline ? ztrdup(line) : line;
+ have_edits = 1;
} else if (!zlemetaline)
free(line);
}
@@ -90,6 +98,10 @@ remember_edits(void)
void
forget_edits(void)
{
+ if (!have_edits) {
+ return;
+ }
+ have_edits = 0;
Histent he;
for (he = hist_ring; he; he = up_histent(he)) {
@@ -1746,7 +1758,8 @@ acceptandinfernexthistory(char **args)
{
Histent he;
- if (!(he = infernexthist(hist_ring, args)))
+ if (virangeflag || !(zlereadflags & ZLRF_HISTORY) ||
+ !(he = infernexthist(hist_ring, args)))
return 1;
zpushnode(bufstack, ztrdup(he->node.nam));
done = 1;
@@ -1758,8 +1771,11 @@ acceptandinfernexthistory(char **args)
int
infernexthistory(char **args)
{
- Histent he = quietgethist(histline);
+ Histent he;
+ if (virangeflag || !(zlereadflags & ZLRF_HISTORY))
+ return 1;
+ he = quietgethist(histline);
if (!he || !(he = infernexthist(he, args)))
return 1;
zle_setline(he);
@@ -1772,12 +1788,14 @@ vifetchhistory(UNUSED(char **args))
{
if (zmult < 0)
return 1;
- if (histline == curhist) {
+ if (histline == curhist || virangeflag || !(zlereadflags & ZLRF_HISTORY)) {
if (!(zmod.flags & MOD_MULT)) {
zlecs = zlell;
zlecs = findbol();
return 0;
}
+ if (virangeflag || !(zlereadflags & ZLRF_HISTORY))
+ return 1;
}
if (!zle_goto_hist((zmod.flags & MOD_MULT) ? zmult : curhist, 0, 0) &&
isset(HISTBEEP)) {
@@ -1921,6 +1939,9 @@ getvisrchstr(void)
int
vihistorysearchforward(char **args)
{
+ if (virangeflag || !(zlereadflags & ZLRF_HISTORY))
+ return 1;
+
if (*args) {
int ose = visrchsense, ret;
char *ost = visrchstr;
@@ -1942,6 +1963,9 @@ vihistorysearchforward(char **args)
int
vihistorysearchbackward(char **args)
{
+ if (virangeflag || !(zlereadflags & ZLRF_HISTORY))
+ return 1;
+
if (*args) {
int ose = visrchsense, ret;
char *ost = visrchstr;
@@ -1967,8 +1991,9 @@ virepeatsearch(UNUSED(char **args))
int n = zmult;
char *zt;
- if (!visrchstr)
+ if (!visrchstr || virangeflag || !(zlereadflags & ZLRF_HISTORY))
return 1;
+
if (zmult < 0) {
n = -n;
visrchsense = -visrchsense;
diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c
index d90838f03..7f31f837c 100644
--- a/Src/Zle/zle_keymap.c
+++ b/Src/Zle/zle_keymap.c
@@ -404,7 +404,7 @@ static void
scankeys(HashNode hn, UNUSED(int flags))
{
Key k = (Key) hn;
- int f = k->nam[0] == Meta ? STOUC(k->nam[1])^32 : STOUC(k->nam[0]);
+ int f = k->nam[0] == Meta ? (unsigned char) k->nam[1]^32 : (unsigned char) k->nam[0];
char m[3];
while(skm_last < f) {
@@ -566,7 +566,7 @@ mod_export int
bindkey(Keymap km, const char *seq, Thingy bind, char *str)
{
Key k;
- int f = seq[0] == Meta ? STOUC(seq[1])^32 : STOUC(seq[0]);
+ int f = seq[0] == Meta ? (unsigned char) seq[1]^32 : (unsigned char) seq[0];
char *buf, *ptr;
if(km->flags & KM_IMMUTABLE)
@@ -661,7 +661,7 @@ keybind(Keymap km, char *seq, char **strp)
Key k;
if(ztrlen(seq) == 1) {
- int f = seq[0] == Meta ? STOUC(seq[1])^32 : STOUC(seq[0]);
+ int f = seq[0] == Meta ? (unsigned char) seq[1]^32 : (unsigned char) seq[0];
Thingy bind = km->first[f];
if(bind)
@@ -687,7 +687,7 @@ keyisprefix(Keymap km, char *seq)
if(!*seq)
return 1;
if(ztrlen(seq) == 1) {
- int f = seq[0] == Meta ? STOUC(seq[1])^32 : STOUC(seq[0]);
+ int f = seq[0] == Meta ? (unsigned char) seq[1]^32 : (unsigned char) seq[0];
if(km->first[f])
return 0;
@@ -745,7 +745,7 @@ bin_bindkey(char *name, char **argv, Options ops, UNUSED(int func))
static struct opn {
char o;
char selp;
- int (*func) _((char *, char *, Keymap, char **, Options, char));
+ int (*func) (char *, char *, Keymap, char **, Options, char);
int min, max;
} const opns[] = {
{ 'l', 0, bin_bindkey_lsmaps, 0, -1 },
@@ -764,10 +764,10 @@ bin_bindkey(char *name, char **argv, Options ops, UNUSED(int func))
int n;
/* select operation and ensure no clashing arguments */
- for(op = opns; op->o && !OPT_ISSET(ops,STOUC(op->o)); op++) ;
+ for(op = opns; op->o && !OPT_ISSET(ops,(unsigned char) op->o); op++) ;
if(op->o)
for(opp = op; (++opp)->o; )
- if(OPT_ISSET(ops,STOUC(opp->o))) {
+ if(OPT_ISSET(ops,(unsigned char) opp->o)) {
zwarnnam(name, "incompatible operation selection options");
return 1;
}
@@ -1049,7 +1049,7 @@ bin_bindkey_bind(char *name, char *kmname, Keymap km, char **argv, Options ops,
char m[3];
if(len < 2 || len > 2 + (bseq[1] == '-') ||
- (first = STOUC(bseq[0])) > (last = STOUC(bseq[len - 1]))) {
+ (first = (unsigned char) bseq[0]) > (last = (unsigned char) bseq[len - 1])) {
zwarnnam(name, "malformed key range `%s'", useq);
ret = 1;
} else {
@@ -1149,8 +1149,8 @@ scanbindlist(char *seq, Thingy bind, char *str, void *magic)
if(bind == bs->bind && (bind || !strcmp(str, bs->str)) &&
ztrlen(seq) == 1 && ztrlen(bs->lastseq) == 1) {
int l = bs->lastseq[1] ?
- STOUC(bs->lastseq[1]) ^ 32 : STOUC(bs->lastseq[0]);
- int t = seq[1] ? STOUC(seq[1]) ^ 32 : STOUC(seq[0]);
+ (unsigned char) bs->lastseq[1] ^ 32 : (unsigned char) bs->lastseq[0];
+ int t = seq[1] ? (unsigned char) seq[1] ^ 32 : (unsigned char) seq[0];
if(t == l + 1) {
zsfree(bs->lastseq);
@@ -1315,7 +1315,7 @@ default_bindings(void)
Keymap vismap = newkeymap(NULL, "visual");
Keymap smap = newkeymap(NULL, ".safe");
Keymap vimaps[2], vilmaps[2], kptr;
- char buf[3], *ed;
+ char buf[3];
int i;
/* vi insert mode and emacs mode: *
@@ -1445,17 +1445,14 @@ default_bindings(void)
}
/* Put the keymaps in the right namespace. The "main" keymap *
- * will be linked to the "emacs" keymap, except that if VISUAL *
- * or EDITOR contain the string "vi" then it will be linked to *
- * the "viins" keymap. */
+ * will be linked to the "emacs" keymap. */
linkkeymap(vmap, "viins", 0);
linkkeymap(emap, "emacs", 0);
linkkeymap(amap, "vicmd", 0);
linkkeymap(oppmap, "viopp", 0);
linkkeymap(vismap, "visual", 0);
linkkeymap(smap, ".safe", 1);
- if (((ed = zgetenv("VISUAL")) && strstr(ed, "vi")) ||
- ((ed = zgetenv("EDITOR")) && strstr(ed, "vi")))
+ if (isset(VIMODE))
linkkeymap(vmap, "main", 0);
else
linkkeymap(emap, "main", 0);
@@ -1526,10 +1523,10 @@ getrestchar_keybuf(void)
*/
while (1) {
if (bufind < buflen) {
- c = STOUC(keybuf[bufind++]);
+ c = (unsigned char) keybuf[bufind++];
if (c == Meta) {
DPUTS(bufind == buflen, "Meta at end of keybuf");
- c = STOUC(keybuf[bufind++]) ^ 32;
+ c = (unsigned char) keybuf[bufind++] ^ 32;
}
} else {
/*
@@ -1586,7 +1583,7 @@ getkeymapcmd(Keymap km, Thingy *funcp, char **strp)
Thingy func = t_undefinedkey;
char *str = NULL;
int lastlen = 0, lastc = lastchar;
- int timeout = 0;
+ int timeout = 0, csi = 0, oscdcs = 0;
keybuflen = 0;
keybuf[0] = 0;
@@ -1636,7 +1633,55 @@ getkeymapcmd(Keymap km, Thingy *funcp, char **strp)
}
#endif
}
- if (!ispfx)
+
+ /* CSI key sequences have a well defined structure so if we currently
+ * have an incomplete one, loop so the rest of it will be included in
+ * the key sequence if that arrives within the timeout. */
+ if (!csi && keybuflen >= 3 && keybuf[keybuflen - 3] == '\033' &&
+ keybuf[keybuflen - 2] == '[')
+ csi = keybuflen - 1;
+ if (csi) {
+ if (keybuf[keybuflen - 2] == Meta || keybuf[keybuflen - 1] < 0x20
+ || keybuf[keybuflen - 1] > 0x3f) {
+ /* If we reach the end of a valid CSI sequence and the matched key
+ * binding is for part of the CSI introduction, select instead the
+ * undefined-key widget and consume the full sequence from the
+ * input buffer. */
+ if (keybuf[keybuflen - 1] >= 0x40 &&
+ keybuf[keybuflen - 1] <= 0x7e && lastlen > csi - 2 &&
+ lastlen <= csi) {
+ if (keybuf[csi] == '?' && (keybuf[keybuflen - 1] == 'c' ||
+ keybuf[keybuflen - 1] == 'u'))
+ { /* is a terminal query response - discard */
+ keybuflen = csi - 2;
+ timeout = csi = 0;
+ continue;
+ }
+ func = t_undefinedkey;
+ lastlen = keybuflen;
+ }
+ csi = 0;
+ }
+ }
+ /* An OSC or DCS sequence is likely a late arriving terminal query
+ * response. Keep looping; if we reach an ST, discard the sequence
+ * - unless we first match a keybinding or a keytimeout elapses. */
+ if (oscdcs) {
+ if (keybuf[keybuflen - 1] == '\007' || /* BEL sometimes used */
+ (keybuf[keybuflen - 2] == '\033' &&
+ keybuf[keybuflen - 1] == '\\') ||
+ (keybuf[keybuflen - 2] == Meta && /* ST can be 0x9b */
+ (unsigned char) keybuf[keybuflen - 1] == (0x9b ^ 32)))
+ {
+ keybuflen = oscdcs - 2; /* discard */
+ timeout = oscdcs = 0;
+ continue;
+ }
+ } else if (keybuflen >= 2 && keybuf[keybuflen - 2] == '\033' &&
+ (keybuf[keybuflen - 1] == ']' || keybuf[keybuflen - 1] == 'P'))
+ oscdcs = keybuflen;
+
+ if (!ispfx && !csi && !oscdcs)
break;
}
if(!lastlen && keybuflen)
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index 9edf30e01..1afb1bf58 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -737,6 +737,7 @@ raw_getbyte(long do_keytmout, char *cptr, int full)
) {
/* Handle the fd. */
char *fdbuf;
+ Thingy save_lbindk = refthingy(lbindk);
{
char buf[BDIGBUFSIZE];
convbase(buf, lwatch_fd->fd, 10);
@@ -779,6 +780,8 @@ raw_getbyte(long do_keytmout, char *cptr, int full)
*/
errtry = 1;
}
+ unrefthingy(lbindk);
+ lbindk = save_lbindk;
}
}
/* Function may have invalidated the display. */
@@ -876,7 +879,7 @@ getbyte(long do_keytmout, int *timeout, int full)
#endif
if (kungetct)
- ret = STOUC(kungetbuf[--kungetct]);
+ ret = (unsigned char) kungetbuf[--kungetct];
else {
for (;;) {
int q = queue_signal_level();
@@ -940,7 +943,7 @@ getbyte(long do_keytmout, int *timeout, int full)
else if (cc == '\n')
cc = '\r';
- ret = STOUC(cc);
+ ret = (unsigned char) cc;
}
/*
* curvichg.buf is raw bytes, not wide characters, so is dealt
@@ -1230,9 +1233,9 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
char *pptbuf;
int pptlen;
- pptbuf = unmetafy(promptexpand(lp ? *lp : NULL, 0, NULL, NULL,
- &pmpt_attr),
+ pptbuf = unmetafy(promptexpand(lp ? *lp : NULL, 0, NULL, NULL),
&pptlen);
+ pmpt_attr = txtcurrentattrs;
write_loop(2, pptbuf, pptlen);
free(pptbuf);
return shingetline();
@@ -1267,10 +1270,13 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
fetchttyinfo = 0;
trashedzle = 0;
raw_lp = lp;
- lpromptbuf = promptexpand(lp ? *lp : NULL, 1, NULL, NULL, &pmpt_attr);
+ txtcurrentattrs = txtpendingattrs = txtunknownattrs = 0;
+ lpromptbuf = promptexpand(lp ? *lp : NULL, 1, NULL, NULL);
+ pmpt_attr = txtcurrentattrs;
raw_rp = rp;
- rpmpt_attr = pmpt_attr;
- rpromptbuf = promptexpand(rp ? *rp : NULL, 1, NULL, NULL, &rpmpt_attr);
+ rpromptbuf = promptexpand(rp ? *rp : NULL, 1, NULL, NULL);
+ rpmpt_attr = txtcurrentattrs;
+ prompt_attr = mixattrs(pmpt_attr, rpmpt_attr);
free_prepostdisplay();
zlereadflags = flags;
@@ -2009,17 +2015,18 @@ reexpandprompt(void)
char *new_lprompt, *new_rprompt;
looping = reexpanding;
- new_lprompt = promptexpand(raw_lp ? *raw_lp : NULL, 1, NULL, NULL,
- &pmpt_attr);
+ txtcurrentattrs = txtpendingattrs = txtunknownattrs = 0;
+ new_lprompt = promptexpand(raw_lp ? *raw_lp : NULL, 1, NULL, NULL);
+ pmpt_attr = txtcurrentattrs;
free(lpromptbuf);
lpromptbuf = new_lprompt;
if (looping != reexpanding)
continue;
- rpmpt_attr = pmpt_attr;
- new_rprompt = promptexpand(raw_rp ? *raw_rp : NULL, 1, NULL, NULL,
- &rpmpt_attr);
+ new_rprompt = promptexpand(raw_rp ? *raw_rp : NULL, 1, NULL, NULL);
+ rpmpt_attr = txtcurrentattrs;
+ prompt_attr = mixattrs(pmpt_attr, rpmpt_attr);
free(rpromptbuf);
rpromptbuf = new_rprompt;
} while (looping != reexpanding);
@@ -2065,6 +2072,8 @@ trashzle(void)
trashedzle = 1;
zrefresh();
showinglist = sl;
+ treplaceattrs(prompt_attr);
+ applytextattributes(0);
moveto(nlnct, 0);
if (clearflag && tccan(TCCLEAREOD)) {
tcout(TCCLEAREOD);
diff --git a/Src/Zle/zle_misc.c b/Src/Zle/zle_misc.c
index eba28d1ec..e17a08d53 100644
--- a/Src/Zle/zle_misc.c
+++ b/Src/Zle/zle_misc.c
@@ -841,9 +841,8 @@ whatcursorposition(UNUSED(char **args))
strcpy(s, mbstr);
s += len;
}
- sprintf(s, " (0%o, %u, 0x%x)", (unsigned int)c,
+ s += sprintf(s, " (0%o, %u, 0x%x)", (unsigned int)c,
(unsigned int)c, (unsigned int)c);
- s += strlen(s);
}
sprintf(s, " point %d of %d(%d%%) column %d", zlecs+1, zlell+1,
zlell ? 100 * zlecs / zlell : 0, zlecs - bol);
diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c
index 30b5d4447..f076bdd61 100644
--- a/Src/Zle/zle_refresh.c
+++ b/Src/Zle/zle_refresh.c
@@ -36,8 +36,8 @@
* non-zero width followed by an arbitrary (but typically small)
* number of characters that have zero width (combining characters).
*
- * The allocated size for each array is given by ?mw_size; nmw_ind
- * is the next free element, i.e. nmwbuf[nmw_ind] will be the next
+ * The allocated size for each array is given by omw_size and nmw_size;
+ * nmw_ind is the next free element, i.e. nmwbuf[nmw_ind] will be the next
* element to be written (we never insert into omwbuf). We initialise
* nmw_ind to 1 to avoid the index stored in the character looking like a
* NULL. This wastees a word but it's safer than messing with pointers.
@@ -149,7 +149,7 @@ char *lpromptbuf, *rpromptbuf;
/* Text attributes after displaying prompts */
/**/
-zattr pmpt_attr, rpmpt_attr;
+zattr pmpt_attr, rpmpt_attr, prompt_attr;
/* number of lines displayed */
@@ -203,12 +203,18 @@ int predisplaylen, postdisplaylen;
/*
- * Attributes used by default on the command line, and
- * attributes for highlighting special (unprintable) characters
- * displayed on screen.
+ * Attributes used by default on the command line,
+ * for highlighting special (unprintable) characters displayed on screen,
+ * and for ellipsis continuation markers.
*/
-static zattr default_atr_on, special_atr_on;
+static zattr default_attr, special_attr, ellipsis_attr;
+
+/*
+ * Layer applied to highlighting for special characters
+ */
+
+static int special_layer;
/*
* Array of region highlights, no special termination.
@@ -245,20 +251,20 @@ char *tcout_func_name;
int cost;
# define SELECT_ADD_COST(X) (cost += X)
-# define zputc(a) (zwcputc(a, NULL), cost++)
+# define zputc(a) (zwcputc(a), cost++)
# define zwrite(a, b) (zwcwrite((a), (b)), \
cost += ((b) * ZLE_CHAR_SIZE))
#else
# define SELECT_ADD_COST(X)
-# define zputc(a) zwcputc(a, NULL)
+# define zputc(a) zwcputc(a)
# define zwrite(a, b) zwcwrite((a), (b))
#endif
-static const REFRESH_ELEMENT zr_cr = { ZWC('\r'), 0 };
+static const REFRESH_ELEMENT zr_cr = { ZWC('\r'), TXT_ERROR };
#ifdef MULTIBYTE_SUPPORT
-static const REFRESH_ELEMENT zr_dt = { ZWC('.'), 0 };
+static const REFRESH_ELEMENT zr_dt = { ZWC('.'), TXT_ERROR };
#endif
-static const REFRESH_ELEMENT zr_nl = { ZWC('\n'), 0 };
+static const REFRESH_ELEMENT zr_nl = { ZWC('\n'), TXT_ERROR };
static const REFRESH_ELEMENT zr_sp = { ZWC(' '), 0 };
static const REFRESH_ELEMENT zr_zr = { ZWC('\0'), 0 };
@@ -269,10 +275,10 @@ static const REFRESH_ELEMENT zr_zr = { ZWC('\0'), 0 };
static const REFRESH_ELEMENT zr_end_ellipsis[] = {
{ ZWC(' '), 0 },
{ ZWC('<'), 0 },
- { ZWC('.'), 0 },
- { ZWC('.'), 0 },
- { ZWC('.'), 0 },
- { ZWC('.'), 0 },
+ { ZWC('.'), TXT_ERROR },
+ { ZWC('.'), TXT_ERROR },
+ { ZWC('.'), TXT_ERROR },
+ { ZWC('.'), TXT_ERROR },
{ ZWC(' '), 0 },
};
#define ZR_END_ELLIPSIS_SIZE \
@@ -281,16 +287,16 @@ static const REFRESH_ELEMENT zr_end_ellipsis[] = {
static const REFRESH_ELEMENT zr_mid_ellipsis1[] = {
{ ZWC(' '), 0 },
{ ZWC('<'), 0 },
- { ZWC('.'), 0 },
- { ZWC('.'), 0 },
- { ZWC('.'), 0 },
- { ZWC('.'), 0 },
+ { ZWC('.'), TXT_ERROR },
+ { ZWC('.'), TXT_ERROR },
+ { ZWC('.'), TXT_ERROR },
+ { ZWC('.'), TXT_ERROR },
};
#define ZR_MID_ELLIPSIS1_SIZE \
((int)(sizeof(zr_mid_ellipsis1)/sizeof(zr_mid_ellipsis1[0])))
static const REFRESH_ELEMENT zr_mid_ellipsis2[] = {
- { ZWC('>'), 0 },
+ { ZWC('>'), TXT_ERROR },
{ ZWC(' '), 0 },
};
#define ZR_MID_ELLIPSIS2_SIZE \
@@ -298,10 +304,10 @@ static const REFRESH_ELEMENT zr_mid_ellipsis2[] = {
static const REFRESH_ELEMENT zr_start_ellipsis[] = {
{ ZWC('>'), 0 },
- { ZWC('.'), 0 },
- { ZWC('.'), 0 },
- { ZWC('.'), 0 },
- { ZWC('.'), 0 },
+ { ZWC('.'), TXT_ERROR },
+ { ZWC('.'), TXT_ERROR },
+ { ZWC('.'), TXT_ERROR },
+ { ZWC('.'), TXT_ERROR },
};
#define ZR_START_ELLIPSIS_SIZE \
((int)(sizeof(zr_start_ellipsis)/sizeof(zr_start_ellipsis[0])))
@@ -316,14 +322,15 @@ static void
zle_set_highlight(void)
{
char **atrs = getaparam("zle_highlight");
- int special_atr_on_set = 0;
- int region_atr_on_set = 0;
- int isearch_atr_on_set = 0;
- int suffix_atr_on_set = 0;
- int paste_atr_on_set = 0;
+ int special_attr_set = 0;
+ int region_attr_set = 0;
+ int isearch_attr_set = 0;
+ int suffix_attr_set = 0;
+ int paste_attr_set = 0;
+ int ellipsis_attr_set = 0;
struct region_highlight *rhp;
- special_atr_on = default_atr_on = 0;
+ special_attr = default_attr = 0;
if (!region_highlights) {
region_highlights = (struct region_highlight *)
zshcalloc(N_SPECIAL_HIGHLIGHTS*sizeof(struct region_highlight));
@@ -336,46 +343,63 @@ zle_set_highlight(void)
}
}
+ /* Default layers */
+ region_highlights[0].layer = 20; /* region */
+ region_highlights[1].layer = 20; /* isearch */
+ region_highlights[2].layer = 10; /* suffix */
+ region_highlights[3].layer = 15; /* paste */
+ special_layer = 30;
+
if (atrs) {
for (; *atrs; atrs++) {
if (!strcmp(*atrs, "none")) {
/* reset attributes for consistency... usually unnecessary */
- special_atr_on = default_atr_on = 0;
- special_atr_on_set = 1;
- paste_atr_on_set = region_atr_on_set =
- isearch_atr_on_set = suffix_atr_on_set = 1;
+ special_attr = default_attr = 0;
+ special_attr_set = 1;
+ paste_attr_set = region_attr_set =
+ isearch_attr_set = suffix_attr_set = 1;
} else if (strpfx("default:", *atrs)) {
- match_highlight(*atrs + 8, &default_atr_on);
+ match_highlight(*atrs + 8, &default_attr, NULL);
} else if (strpfx("special:", *atrs)) {
- match_highlight(*atrs + 8, &special_atr_on);
- special_atr_on_set = 1;
+ match_highlight(*atrs + 8, &special_attr, &special_layer);
+ special_attr_set = 1;
} else if (strpfx("region:", *atrs)) {
- match_highlight(*atrs + 7, &region_highlights[0].atr);
- region_atr_on_set = 1;
+ match_highlight(*atrs + 7, &(region_highlights[0].atr),
+ &(region_highlights[0].layer));
+ region_attr_set = 1;
} else if (strpfx("isearch:", *atrs)) {
- match_highlight(*atrs + 8, &(region_highlights[1].atr));
- isearch_atr_on_set = 1;
+ match_highlight(*atrs + 8, &(region_highlights[1].atr),
+ &(region_highlights[1].layer));
+ isearch_attr_set = 1;
} else if (strpfx("suffix:", *atrs)) {
- match_highlight(*atrs + 7, &(region_highlights[2].atr));
- suffix_atr_on_set = 1;
+ match_highlight(*atrs + 7, &(region_highlights[2].atr),
+ &(region_highlights[2].layer));
+ suffix_attr_set = 1;
} else if (strpfx("paste:", *atrs)) {
- match_highlight(*atrs + 6, &(region_highlights[3].atr));
- paste_atr_on_set = 1;
+ match_highlight(*atrs + 6, &(region_highlights[3].atr),
+ &(region_highlights[3].layer));
+ paste_attr_set = 1;
+ } else if (strpfx("ellipsis:", *atrs)) {
+ match_highlight(*atrs + 9, &ellipsis_attr, NULL);
+ ellipsis_attr_set = 1;
}
}
}
- /* Defaults */
- if (!special_atr_on_set)
- special_atr_on = TXTSTANDOUT;
- if (!region_atr_on_set)
+ /* Default attributes */
+ if (!special_attr_set)
+ special_attr = TXTSTANDOUT;
+ if (!region_attr_set)
region_highlights[0].atr = TXTSTANDOUT;
- if (!isearch_atr_on_set)
+ if (!isearch_attr_set)
region_highlights[1].atr = TXTUNDERLINE;
- if (!suffix_atr_on_set)
+ if (!suffix_attr_set)
region_highlights[2].atr = TXTBOLDFACE;
- if (!paste_atr_on_set)
+ if (!paste_attr_set)
region_highlights[3].atr = TXTSTANDOUT;
+ if (!ellipsis_attr_set)
+ ellipsis_attr = TXTBGCOLOUR | ((zattr) 3 << TXT_ATTR_BG_COL_SHIFT) |
+ TXTFGCOLOUR | ((zattr) 4 << TXT_ATTR_FG_COL_SHIFT);
allocate_colour_buffer();
}
@@ -400,14 +424,13 @@ zle_free_highlight(void)
char **
get_region_highlight(UNUSED(Param pm))
{
- int arrsize = n_region_highlights;
+ int arrsize = n_region_highlights - N_SPECIAL_HIGHLIGHTS;
char **retarr, **arrp;
struct region_highlight *rhp;
/* region_highlights may not have been set yet */
- if (!arrsize)
+ if (!n_region_highlights)
return hmkarray(NULL);
- arrsize -= N_SPECIAL_HIGHLIGHTS;
DPUTS(arrsize < 0, "arrsize is negative from n_region_highlights");
arrp = retarr = (char **)zhalloc((arrsize+1)*sizeof(char *));
@@ -415,20 +438,18 @@ get_region_highlight(UNUSED(Param pm))
for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
arrsize--;
rhp++, arrp++) {
- char digbuf1[DIGBUFSIZE], digbuf2[DIGBUFSIZE];
- int atrlen, alloclen;
- const char memo_equals[] = "memo=";
-
- sprintf(digbuf1, "%d", rhp->start);
- sprintf(digbuf2, "%d", rhp->end);
-
- atrlen = output_highlight(rhp->atr, NULL);
- alloclen = atrlen + strlen(digbuf1) + strlen(digbuf2) +
- 3; /* 2 spaces, 1 terminating NUL */
+ char digbuf[2 * DIGBUFSIZE], layerbuf[7 + DIGBUFSIZE];
+ int offset;
+ const char memo_equals[] = " memo=";
+ int alloclen = sprintf(digbuf, "%d %d", rhp->start, rhp->end) +
+ output_highlight(rhp->atr, NULL) +
+ 2; /* space and terminating NUL */
if (rhp->flags & ZRH_PREDISPLAY)
- alloclen += 2; /* "P " */
+ alloclen++; /* "P" */
+ if (rhp->layer != 10)
+ alloclen += sprintf(layerbuf, ",layer=%d", rhp->layer);
if (rhp->memo)
- alloclen += 1 /* space */ + strlen(memo_equals) + strlen(rhp->memo);
+ alloclen += sizeof(memo_equals) - 1 + strlen(rhp->memo);
*arrp = (char *)zhalloc(alloclen * sizeof(char));
/*
* On input we allow a space after the flags.
@@ -437,13 +458,14 @@ get_region_highlight(UNUSED(Param pm))
* into three words, and then check the first to
* see if there are flags. However, it's arguable.
*/
- sprintf(*arrp, "%s%s %s ",
+ offset = sprintf(*arrp, "%s%s ",
(rhp->flags & ZRH_PREDISPLAY) ? "P" : "",
- digbuf1, digbuf2);
- (void)output_highlight(rhp->atr, *arrp + strlen(*arrp));
+ digbuf);
+ (void)output_highlight(rhp->atr, *arrp + offset);
+ if (rhp->layer != 10)
+ strcat(*arrp, layerbuf);
if (rhp->memo) {
- strcat(*arrp, " ");
strcat(*arrp, memo_equals);
strcat(*arrp, rhp->memo);
}
@@ -452,12 +474,10 @@ get_region_highlight(UNUSED(Param pm))
return retarr;
}
-
/*
* The parameter system requires the pm argument, but this
* may be NULL if called directly.
*/
-
/**/
void
set_region_highlight(UNUSED(Param pm), char **aval)
@@ -516,7 +536,8 @@ set_region_highlight(UNUSED(Param pm), char **aval)
while (inblank(*strp))
strp++;
- strp = (char*) match_highlight(strp, &rhp->atr);
+ rhp->layer = 10; /* default */
+ strp = (char*) match_highlight(strp, &rhp->atr, &rhp->layer);
while (inblank(*strp))
strp++;
@@ -571,22 +592,6 @@ unset_region_highlight(Param pm, int exp)
}
-/* The last attributes that were on. */
-static zattr lastatr;
-
-/*
- * Clear the last attributes that we set: used when we're going
- * to be outputting stuff that shouldn't show up as text.
- */
-static void
-clearattributes(void)
-{
- if (lastatr) {
- settextattributes(TXT_ATTR_OFF_FROM_ON(lastatr));
- lastatr = 0;
- }
-}
-
/*
* Output a termcap capability, clearing any text attributes so
* as not to mess up the display.
@@ -595,7 +600,7 @@ clearattributes(void)
static void
tcoutclear(int cap)
{
- clearattributes();
+ cleartextattributes(0);
tcout(cap);
}
@@ -603,47 +608,20 @@ tcoutclear(int cap)
* Output the character. This must come from the new video
* buffer, nbuf, since we access the multiword buffer nmwbuf
* directly.
- *
- * curatrp may be NULL, otherwise points to an integer specifying
- * what attributes were turned on for a character output immediately
- * before, in order to optimise output of attribute changes.
*/
/**/
void
-zwcputc(const REFRESH_ELEMENT *c, zattr *curatrp)
+zwcputc(const REFRESH_ELEMENT *c)
{
- /*
- * Safety: turn attributes off if last heard of turned on.
- * This differs from *curatrp, which is an optimisation for
- * writing lots of stuff at once.
- */
#ifdef MULTIBYTE_SUPPORT
mbstate_t mbstate;
int i;
VARARR(char, mbtmp, MB_CUR_MAX + 1);
#endif
- if (lastatr & ~c->atr) {
- /* Stuff on we don't want, turn it off */
- settextattributes(TXT_ATTR_OFF_FROM_ON(lastatr & ~c->atr));
- lastatr = 0;
- }
-
- /*
- * Don't output "on" attributes in a string of characters with
- * the same attributes. Be careful in case a different colour
- * needs setting.
- */
- if ((c->atr & TXT_ATTR_ON_MASK) &&
- (!curatrp ||
- ((*curatrp & TXT_ATTR_ON_VALUES_MASK) !=
- (c->atr & TXT_ATTR_ON_VALUES_MASK)))) {
- /* Record just the control flags we might need to turn off... */
- lastatr = c->atr & TXT_ATTR_ON_MASK;
- /* ...but set including the values for colour attributes */
- settextattributes(c->atr & TXT_ATTR_ON_VALUES_MASK);
- }
+ treplaceattrs(c->atr);
+ applytextattributes(0);
#ifdef MULTIBYTE_SUPPORT
if (c->atr & TXT_MULTIWORD_MASK) {
@@ -664,35 +642,15 @@ zwcputc(const REFRESH_ELEMENT *c, zattr *curatrp)
#else
fputc(c->chr, shout);
#endif
-
- /*
- * Always output "off" attributes since we only turn off at
- * the end of a chunk of highlighted text.
- */
- if (c->atr & TXT_ATTR_OFF_MASK) {
- settextattributes(c->atr & TXT_ATTR_OFF_MASK);
- lastatr &= ~((c->atr & TXT_ATTR_OFF_MASK) >> TXT_ATTR_OFF_ON_SHIFT);
- }
- if (curatrp) {
- /*
- * Remember the current attributes: those that are turned
- * on, less those that are turned off again. Include
- * colour attributes here in case the colour changes to
- * another non-default one.
- */
- *curatrp = (c->atr & TXT_ATTR_ON_VALUES_MASK) &
- ~((c->atr & TXT_ATTR_OFF_MASK) >> TXT_ATTR_OFF_ON_SHIFT);
- }
}
static int
zwcwrite(const REFRESH_STRING s, size_t i)
{
size_t j;
- zattr curatr = 0;
for (j = 0; j < i; j++)
- zwcputc(s + j, &curatr);
+ zwcputc(s + j);
return i; /* TODO something better for error indication */
}
@@ -939,29 +897,6 @@ snextline(Rparams rpms)
rpms->sen = rpms->s + winw;
}
-
-/**/
-static void
-settextattributes(zattr atr)
-{
- if (txtchangeisset(atr, TXTNOBOLDFACE))
- tsetcap(TCALLATTRSOFF, 0);
- if (txtchangeisset(atr, TXTNOSTANDOUT))
- tsetcap(TCSTANDOUTEND, 0);
- if (txtchangeisset(atr, TXTNOUNDERLINE))
- tsetcap(TCUNDERLINEEND, 0);
- if (txtchangeisset(atr, TXTBOLDFACE))
- tsetcap(TCBOLDFACEBEG, 0);
- if (txtchangeisset(atr, TXTSTANDOUT))
- tsetcap(TCSTANDOUTBEG, 0);
- if (txtchangeisset(atr, TXTUNDERLINE))
- tsetcap(TCUNDERLINEBEG, 0);
- if (txtchangeisset(atr, TXTFGCOLOUR|TXTNOFGCOLOUR))
- set_colour_attribute(atr, COL_SEQ_FG, 0);
- if (txtchangeisset(atr, TXTBGCOLOUR|TXTNOBGCOLOUR))
- set_colour_attribute(atr, COL_SEQ_BG, 0);
-}
-
#ifdef MULTIBYTE_SUPPORT
/*
* Add a multiword glyph at the screen location base.
@@ -1043,7 +978,6 @@ zrefresh(void)
int tmppos; /* t - tmpline */
int tmpalloced; /* flag to free tmpline when finished */
int remetafy; /* flag that zle line is metafied */
- zattr txtchange; /* attributes set after prompts */
int rprompt_off = 1; /* Offset of rprompt from right of screen */
struct rparams rpms;
#ifdef MULTIBYTE_SUPPORT
@@ -1192,9 +1126,7 @@ zrefresh(void)
#endif
/* we probably should only have explicitly set attributes */
tsetcap(TCALLATTRSOFF, 0);
- tsetcap(TCSTANDOUTEND, 0);
- tsetcap(TCUNDERLINEEND, 0);
- txtattrmask = 0;
+ txtcurrentattrs = txtpendingattrs = txtunknownattrs = 0;
if (trashedzle && !clearflag)
reexpandprompt();
@@ -1218,9 +1150,8 @@ zrefresh(void)
zputs(lpromptbuf, shout);
if (lpromptwof == winw)
zputs("\n", shout); /* works with both hasam and !hasam */
- } else {
- txtchange = pmpt_attr;
- settextattributes(txtchange);
+ /* lpromptbuf includes literal escapes so we need to update for it */
+ txtcurrentattrs = txtpendingattrs = pmpt_attr;
}
if (clearflag) {
zputc(&zr_cr);
@@ -1263,45 +1194,40 @@ zrefresh(void)
rpms.s = nbuf[rpms.ln = 0] + lpromptw;
rpms.sen = *nbuf + winw;
for (t = tmpline, tmppos = 0; tmppos < tmpll; t++, tmppos++) {
- unsigned ireg;
- zattr base_atr_on = default_atr_on, base_atr_off = 0;
- zattr all_atr_on, all_atr_off;
+ zattr base_attr = mixattrs(default_attr, prompt_attr);
+ zattr all_attr;
struct region_highlight *rhp;
+ int layer, nextlayer = 0;
/*
* Calculate attribute based on region.
*/
- for (ireg = 0, rhp = region_highlights;
- ireg < n_region_highlights;
- ireg++, rhp++) {
- int offset;
- if (rhp->flags & ZRH_PREDISPLAY)
- offset = 0; /* include predisplay in start end */
- else
- offset = predisplaylen; /* increment over it */
- if (rhp->start + offset <= tmppos &&
- tmppos < rhp->end + offset) {
- if (rhp->atr & (TXTFGCOLOUR|TXTBGCOLOUR)) {
- /* override colour with later entry */
- base_atr_on = (base_atr_on & ~TXT_ATTR_ON_VALUES_MASK) |
- rhp->atr;
- } else {
- /* no colour set yet */
- base_atr_on |= rhp->atr;
+ do {
+ unsigned ireg;
+ layer = nextlayer;
+ nextlayer = special_layer;
+ for (ireg = 0, rhp = region_highlights;
+ ireg < n_region_highlights;
+ ireg++, rhp++) {
+ if (rhp->layer == layer) {
+ int offset;
+ if (rhp->flags & ZRH_PREDISPLAY)
+ offset = 0; /* include predisplay in start end */
+ else
+ offset = predisplaylen; /* increment over it */
+ if (rhp->start + offset <= tmppos &&
+ tmppos < rhp->end + offset) {
+ base_attr = mixattrs(rhp->atr, base_attr);
+ if (layer > special_layer)
+ all_attr = mixattrs(rhp->atr, all_attr);
+ }
+ } else if (rhp->layer > layer && rhp->layer < nextlayer) {
+ nextlayer = rhp->layer;
}
- if (tmppos == rhp->end + offset - 1 ||
- tmppos == tmpll - 1)
- base_atr_off |= TXT_ATTR_OFF_FROM_ON(rhp->atr);
}
- }
- if (special_atr_on & (TXTFGCOLOUR|TXTBGCOLOUR)) {
- /* keep colours from special attributes */
- all_atr_on = special_atr_on |
- (base_atr_on & ~TXT_ATTR_COLOUR_ON_MASK);
- } else {
- /* keep colours from standard attributes */
- all_atr_on = special_atr_on | base_atr_on;
- }
- all_atr_off = TXT_ATTR_OFF_FROM_ON(all_atr_on);
+ if (special_layer == layer) {
+ all_attr = mixattrs(special_attr, base_attr);
+ }
+ } while (nextlayer > layer);
if (t == scs) /* if cursor is here, remember it */
rpms.nvcs = rpms.s - nbuf[rpms.nvln = rpms.ln];
@@ -1319,10 +1245,9 @@ zrefresh(void)
} else {
do {
rpms.s->chr = ZWC(' ');
- rpms.s->atr = base_atr_on;
+ rpms.s->atr = base_attr;
rpms.s++;
} while ((++t0) & 7);
- rpms.s[-1].atr |= base_atr_off;
}
}
#ifdef MULTIBYTE_SUPPORT
@@ -1341,11 +1266,9 @@ zrefresh(void)
rpms.s->chr = ZWC(' ');
if (!started)
started = 1;
- rpms.s->atr = all_atr_on;
+ rpms.s->atr = all_attr;
rpms.s++;
} while (rpms.s < rpms.sen);
- if (started)
- rpms.s[-1].atr |= all_atr_off;
if (nextline(&rpms, 1))
break;
if (t == scs) {
@@ -1369,15 +1292,11 @@ zrefresh(void)
* occurrence.
*/
rpms.s->chr = ZWC('?');
- rpms.s->atr = all_atr_on | all_atr_off;
+ rpms.s->atr = all_attr;
rpms.s++;
} else {
/* We can fit it without reaching the end of the line. */
- /*
- * As we don't actually output the WEOF, we attach
- * any off attributes to the character itself.
- */
- rpms.s->atr = base_atr_on | base_atr_off;
+ rpms.s->atr = base_attr;
if (ichars > 1) {
/*
* Glyph includes combining characters.
@@ -1393,7 +1312,7 @@ zrefresh(void)
while (--width > 0) {
rpms.s->chr = WEOF;
/* Not used, but be consistent... */
- rpms.s->atr = base_atr_on | base_atr_off;
+ rpms.s->atr = base_attr;
rpms.s++;
}
}
@@ -1410,17 +1329,16 @@ zrefresh(void)
#endif
) { /* other control character */
rpms.s->chr = ZWC('^');
- rpms.s->atr = all_atr_on;
+ rpms.s->atr = all_attr;
rpms.s++;
if (rpms.s == rpms.sen) {
/* text wrapped */
- rpms.s[-1].atr |= all_atr_off;
if (nextline(&rpms, 1))
break;
}
rpms.s->chr = (((unsigned int)*t & ~0x80u) > 31) ?
ZWC('?') : (*t | ZWC('@'));
- rpms.s->atr = all_atr_on | all_atr_off;
+ rpms.s->atr = all_attr;
rpms.s++;
}
#ifdef MULTIBYTE_SUPPORT
@@ -1432,7 +1350,6 @@ zrefresh(void)
char dispchars[11];
char *dispptr = dispchars;
wchar_t wc;
- int started = 0;
#ifdef __STDC_ISO_10646__
if (ZSH_INVALID_WCHAR_TEST(*t)) {
@@ -1449,31 +1366,23 @@ zrefresh(void)
if (mbtowc(&wc, dispptr, 1) == 1 /* paranoia */)
{
rpms.s->chr = wc;
- if (!started)
- started = 1;
- rpms.s->atr = all_atr_on;
+ rpms.s->atr = all_attr;
rpms.s++;
if (rpms.s == rpms.sen) {
/* text wrapped */
- if (started) {
- rpms.s[-1].atr |= all_atr_off;
- started = 0;
- }
if (nextline(&rpms, 1))
break;
}
}
dispptr++;
}
- if (started)
- rpms.s[-1].atr |= all_atr_off;
if (*dispptr) /* nextline said stop processing */
break;
}
#else
else { /* normal character */
rpms.s->chr = *t;
- rpms.s->atr = base_atr_on | base_atr_off;
+ rpms.s->atr = base_attr;
rpms.s++;
}
#endif
@@ -1499,13 +1408,12 @@ zrefresh(void)
if (statusline) {
int outll, outsz;
- zattr all_atr_on, all_atr_off;
+ zattr all_attr;
char *statusdup = ztrdup(statusline);
ZLE_STRING_T outputline =
stringaszleline(statusdup, 0, &outll, &outsz, NULL);
- all_atr_on = special_atr_on;
- all_atr_off = TXT_ATTR_OFF_FROM_ON(all_atr_on);
+ all_attr = special_attr;
rpms.tosln = rpms.ln + 1;
nbuf[rpms.ln][winw + 1] = zr_zr; /* text not wrapped */
@@ -1525,7 +1433,7 @@ zrefresh(void)
}
if (width > rpms.sen - rpms.s) {
rpms.s->chr = ZWC('?');
- rpms.s->atr = all_atr_on | all_atr_off;
+ rpms.s->atr = all_attr;
rpms.s++;
} else {
rpms.s->chr = *u;
@@ -1542,7 +1450,7 @@ zrefresh(void)
#endif
if (ZC_icntrl(*u)) { /* simplified processing in the status line */
rpms.s->chr = ZWC('^');
- rpms.s->atr = all_atr_on;
+ rpms.s->atr = all_attr;
rpms.s++;
if (rpms.s == rpms.sen) {
nbuf[rpms.ln][winw + 1] = zr_nl;/* text wrapped */
@@ -1550,7 +1458,7 @@ zrefresh(void)
}
rpms.s->chr = (((unsigned int)*u & ~0x80u) > 31)
? ZWC('?') : (*u | ZWC('@'));
- rpms.s->atr = all_atr_on | all_atr_off;
+ rpms.s->atr = all_attr;
rpms.s++;
} else {
rpms.s->chr = *u;
@@ -1603,6 +1511,7 @@ zrefresh(void)
}
#endif
ZR_memcpy(rpms.sen, zr_end_ellipsis, ZR_END_ELLIPSIS_SIZE);
+ rpms.sen[1].atr = ellipsis_attr;
#ifdef MULTIBYTE_SUPPORT
/* Extend to the end if we backed off for a wide character */
if (extra_ellipsis) {
@@ -1638,6 +1547,7 @@ zrefresh(void)
}
#endif
ZR_memcpy(rpms.sen, zr_mid_ellipsis1, ZR_MID_ELLIPSIS1_SIZE);
+ rpms.sen[1].atr = ellipsis_attr;
rpms.sen += ZR_MID_ELLIPSIS1_SIZE;
#ifdef MULTIBYTE_SUPPORT
/* Extend if we backed off for a wide character */
@@ -1647,6 +1557,7 @@ zrefresh(void)
}
#endif
ZR_memcpy(rpms.sen, zr_mid_ellipsis2, ZR_MID_ELLIPSIS2_SIZE);
+ rpms.sen[1].atr = prompt_attr;
nbuf[rpms.tosln][winw] = nbuf[rpms.tosln][winw + 1] = zr_zr;
}
@@ -1679,7 +1590,9 @@ zrefresh(void)
t0 = winw - lpromptw;
t0 = t0 > ZR_START_ELLIPSIS_SIZE ? ZR_START_ELLIPSIS_SIZE : t0;
ZR_memcpy(nbuf[0] + lpromptw, zr_start_ellipsis, t0);
+ (*nbuf + lpromptw)->atr = ellipsis_attr;
ZR_memset(nbuf[0] + lpromptw + t0, zr_sp, winw - t0 - lpromptw);
+ (*nbuf + lpromptw + t0)->atr = prompt_attr;
nbuf[0][winw] = nbuf[0][winw + 1] = zr_zr;
}
@@ -1725,9 +1638,9 @@ zrefresh(void)
/* output the right-prompt if appropriate */
if (put_rpmpt && !iln && !oput_rpmpt) {
- zattr attrchange;
-
moveto(0, winw - rprompt_off - rpromptw);
+ treplaceattrs(pmpt_attr);
+ applytextattributes(0);
zputs(rpromptbuf, shout);
if (rprompt_off) {
vcs = winw - rprompt_off;
@@ -1735,39 +1648,7 @@ zrefresh(void)
zputc(&zr_cr);
vcs = 0;
}
- /* reset character attributes to that set by the main prompt */
- txtchange = pmpt_attr;
- /*
- * Keep attributes that have actually changed,
- * which are ones off in rpmpt_attr and on in
- * pmpt_attr, and vice versa.
- */
- attrchange = txtchange &
- (TXT_ATTR_OFF_FROM_ON(rpmpt_attr) |
- TXT_ATTR_ON_FROM_OFF(rpmpt_attr));
- /*
- * Careful in case the colour changed.
- */
- if (txtchangeisset(txtchange, TXTFGCOLOUR) &&
- (!txtchangeisset(rpmpt_attr, TXTFGCOLOUR) ||
- ((txtchange ^ rpmpt_attr) & TXT_ATTR_FG_COL_MASK)))
- {
- attrchange |=
- txtchange & (TXTFGCOLOUR | TXT_ATTR_FG_COL_MASK);
- }
- if (txtchangeisset(txtchange, TXTBGCOLOUR) &&
- (!txtchangeisset(rpmpt_attr, TXTBGCOLOUR) ||
- ((txtchange ^ rpmpt_attr) & TXT_ATTR_BG_COL_MASK)))
- {
- attrchange |=
- txtchange & (TXTBGCOLOUR | TXT_ATTR_BG_COL_MASK);
- }
- /*
- * Now feed these changes into the usual function,
- * if necessary.
- */
- if (attrchange)
- settextattributes(attrchange);
+ txtcurrentattrs = txtpendingattrs = rpmpt_attr;
}
}
@@ -1780,11 +1661,6 @@ individually */
refreshline(iln);
}
-/* reset character attributes */
- if (clearf && postedit) {
- if ((txtchange = pmpt_attr ? pmpt_attr : rpmpt_attr))
- settextattributes(txtchange);
- }
clearf = 0;
oput_rpmpt = put_rpmpt;
@@ -1984,8 +1860,6 @@ refreshline(int ln)
/* 3: main display loop - write out the buffer using whatever tricks we can */
for (;;) {
- zattr now_off;
-
#ifdef MULTIBYTE_SUPPORT
if ((!nl->chr || nl->chr != WEOF) && (!ol->chr || ol->chr != WEOF)) {
#endif
@@ -2087,7 +1961,7 @@ refreshline(int ln)
* deletions, so turn off text attributes.
*/
if (first) {
- clearattributes();
+ cleartextattributes(0);
first = 0;
}
tc_delchars(i);
@@ -2176,13 +2050,8 @@ refreshline(int ln)
break;
do {
#endif
- /*
- * If an attribute was on here but isn't any more,
- * output the sequence to turn it off.
- */
- now_off = ol->atr & ~nl->atr & TXT_ATTR_ON_MASK;
- if (now_off)
- settextattributes(TXT_ATTR_OFF_FROM_ON(now_off));
+ treplaceattrs(nl->atr);
+ applytextattributes(0);
/*
* This is deliberately called if nl->chr is WEOF
@@ -2560,8 +2429,8 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs)
for (t0 = 0; t0 < tmpll; t0++) {
unsigned ireg;
- zattr base_atr_on = 0, base_atr_off = 0;
- zattr all_atr_on, all_atr_off;
+ zattr base_attr = 0;
+ zattr all_attr;
struct region_highlight *rhp;
/*
* Calculate attribute based on region.
@@ -2576,38 +2445,28 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs)
offset = predisplaylen; /* increment over it */
if (rhp->start + offset <= t0 &&
t0 < rhp->end + offset) {
- if (base_atr_on & (TXTFGCOLOUR|TXTBGCOLOUR)) {
+ if (base_attr & (TXTFGCOLOUR|TXTBGCOLOUR)) {
/* keep colour already set */
- base_atr_on |= rhp->atr & ~TXT_ATTR_COLOUR_ON_MASK;
+ base_attr |= rhp->atr & ~TXT_ATTR_COLOUR_MASK;
} else {
/* no colour set yet */
- base_atr_on |= rhp->atr;
+ base_attr |= rhp->atr;
}
- if (t0 == rhp->end + offset - 1 ||
- t0 == tmpll - 1)
- base_atr_off |= TXT_ATTR_OFF_FROM_ON(rhp->atr);
}
}
- if (special_atr_on & (TXTFGCOLOUR|TXTBGCOLOUR)) {
- /* keep colours from special attributes */
- all_atr_on = special_atr_on |
- (base_atr_on & ~TXT_ATTR_COLOUR_ON_MASK);
- } else {
- /* keep colours from standard attributes */
- all_atr_on = special_atr_on | base_atr_on;
- }
- all_atr_off = TXT_ATTR_OFF_FROM_ON(all_atr_on);
+ all_attr = mixattrs(special_attr, base_attr);
+ if (t0 == tmpcs)
+ nvcs = vp - vbuf;
if (tmpline[t0] == ZWC('\t')) {
for (*vp++ = zr_sp; (vp - vbuf) & 7; )
*vp++ = zr_sp;
- vp[-1].atr |= base_atr_off;
} else if (tmpline[t0] == ZWC('\n')) {
vp->chr = ZWC('\\');
- vp->atr = all_atr_on;
+ vp->atr = all_attr;
vp++;
vp->chr = ZWC('n');
- vp->atr = all_atr_on | all_atr_off;
+ vp->atr = all_attr;
vp++;
#ifdef MULTIBYTE_SUPPORT
} else if (WC_ISPRINT(tmpline[t0]) &&
@@ -2623,7 +2482,7 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs)
}
} else
ichars = 1;
- vp->atr = base_atr_on | base_atr_off;
+ vp->atr = base_attr;
if (ichars > 1)
addmultiword(vp, tmpline+t0, ichars);
else
@@ -2631,7 +2490,7 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs)
vp++;
while (--width > 0) {
vp->chr = WEOF;
- vp->atr = base_atr_on | base_atr_off;
+ vp->atr = base_attr;
vp++;
}
t0 += ichars - 1;
@@ -2641,14 +2500,14 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs)
&& (unsigned)tmpline[t0] <= 0xffU
#endif
) {
- ZLE_INT_T t = tmpline[++t0];
+ ZLE_INT_T t = tmpline[t0];
vp->chr = ZWC('^');
- vp->atr = all_atr_on;
+ vp->atr = all_attr;
vp++;
vp->chr = (((unsigned int)t & ~0x80u) > 31) ?
ZWC('?') : (t | ZWC('@'));
- vp->atr = all_atr_on | all_atr_off;
+ vp->atr = all_attr;
vp++;
}
#ifdef MULTIBYTE_SUPPORT
@@ -2656,7 +2515,6 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs)
char dispchars[11];
char *dispptr = dispchars;
wchar_t wc;
- int started = 0;
if ((unsigned)tmpline[t0] > 0xffffU) {
sprintf(dispchars, "<%.08x>", (unsigned)tmpline[t0]);
@@ -2666,25 +2524,19 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs)
while (*dispptr) {
if (mbtowc(&wc, dispptr, 1) == 1 /* paranoia */) {
vp->chr = wc;
- if (!started)
- started = 1;
- vp->atr = all_atr_on;
+ vp->atr = all_attr;
vp++;
}
dispptr++;
}
- if (started)
- vp[-1].atr |= all_atr_off;
}
#else
else {
vp->chr = tmpline[t0];
- vp->atr = base_atr_on | base_atr_off;
+ vp->atr = base_attr;
vp++;
}
#endif
- if (t0 == tmpcs)
- nvcs = vp - vbuf - 1;
}
if (t0 == tmpcs)
nvcs = vp - vbuf;
@@ -2699,11 +2551,11 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs)
}
if (winpos) {
vbuf[winpos].chr = ZWC('<'); /* line continues to the left */
- vbuf[winpos].atr = 0;
+ vbuf[winpos].atr = ellipsis_attr;
}
if ((int)ZR_strlen(vbuf + winpos) > (winw - hasam)) {
vbuf[winpos + winw - hasam - 1].chr = ZWC('>'); /* line continues to right */
- vbuf[winpos + winw - hasam - 1].atr = 0;
+ vbuf[winpos + winw - hasam - 1].atr = ellipsis_attr;
vbuf[winpos + winw - hasam] = zr_zr;
}
ZR_strcpy(nbuf[0], vbuf + winpos);
diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c
index cd3f2c356..f71435b01 100644
--- a/Src/Zle/zle_thingy.c
+++ b/Src/Zle/zle_thingy.c
@@ -344,7 +344,7 @@ bin_zle(char *name, char **args, Options ops, UNUSED(int func))
{
static struct opn {
char o;
- int (*func) _((char *, char **, Options, char));
+ int (*func) (char *, char **, Options, char);
int min, max;
} const opns[] = {
{ 'l', bin_zle_list, 0, -1 },
@@ -366,10 +366,10 @@ bin_zle(char *name, char **args, Options ops, UNUSED(int func))
int n;
/* select operation and ensure no clashing arguments */
- for(op = opns; op->o && !OPT_ISSET(ops,STOUC(op->o)); op++) ;
+ for(op = opns; op->o && !OPT_ISSET(ops, (unsigned char) op->o); op++) ;
if(op->o)
for(opp = op; (++opp)->o; )
- if(OPT_ISSET(ops,STOUC(opp->o))) {
+ if(OPT_ISSET(ops, (unsigned char) opp->o)) {
zwarnnam(name, "incompatible operation selection options");
return 1;
}
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index fdd168763..aa3c71bc2 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -576,7 +576,7 @@ parambeg(char *s)
while (idigit(*e))
e++;
else
- e = itype_end(e, IIDENT, 0);
+ e = itype_end(e, INAMESPC, 0);
/* Now make sure that the cursor is inside the name. */
if (offs <= e - s && offs >= b - s && n <= 0) {
@@ -765,7 +765,7 @@ docomplete(int lst)
else if (idigit(*q))
do q++; while (idigit(*q));
else
- q = itype_end(q, IIDENT, 0);
+ q = itype_end(q, INAMESPC, 0);
sav = *q;
*q = '\0';
if (zlemetacs - wb == q - s &&
@@ -1315,6 +1315,8 @@ get_comp_string(void)
ins = (tok == REPEAT ? 2 : (tok != STRING && tok != TYPESET));
zsfree(cmdstr);
cmdstr = ztrdup(tokstr);
+ untokenize(cmdstr);
+ remnulargs(cmdstr);
cmdtok = tok;
/*
* If everything before is a redirection, or anything
@@ -1497,7 +1499,8 @@ get_comp_string(void)
if (varq)
tt = clwords[clwpos];
- s = itype_end(tt, IIDENT, 0);
+ /* The only place we complete namespaces, see IIDENT below */
+ s = itype_end(tt, INAMESPC, 0);
sav = *s;
*s = '\0';
zsfree(varname);
@@ -1568,6 +1571,8 @@ get_comp_string(void)
i = 0;
MB_METACHARINIT();
+ /* All further uses of IIDENT in this file should change to *
+ * INAMESPC if this case is changed. Too ugly to risk now. */
if (itype_end(s, IIDENT, 1) == s)
nnb = s + MB_METACHARLEN(s);
else
@@ -1641,7 +1646,7 @@ get_comp_string(void)
} else {
/* In mathematical expression, we complete parameter names *
* (even if they don't have a `$' in front of them). So we *
- * have to find that name. */
+ * have to find that name. See above regarding INAMESPC */
char *cspos = zlemetaline + zlemetacs, *wptr, *cptr;
we = itype_end(cspos, IIDENT, 0) - zlemetaline;
@@ -2426,7 +2431,7 @@ mod_export int
printfmt(char *fmt, int n, int dopr, int doesc)
{
char *p = fmt, nc[DIGBUFSIZE];
- int l = 0, cc = 0, b = 0, s = 0, u = 0, m;
+ int l = 0, cc = 0;
MB_METACHARINIT();
for (; *p; ) {
@@ -2437,48 +2442,45 @@ printfmt(char *fmt, int n, int dopr, int doesc)
if (idigit(*++p))
arg = zstrtol(p, &p, 10);
if (*p) {
- m = 0;
switch (*p) {
case '%':
- if (dopr)
+ if (dopr) {
+ applytextattributes(0);
putc('%', shout);
+ }
cc++;
break;
case 'n':
sprintf(nc, "%d", n);
- if (dopr)
+ if (dopr) {
+ applytextattributes(0);
fputs(nc, shout);
+ }
cc += MB_METASTRWIDTH(nc);
break;
case 'B':
- b = 1;
if (dopr)
- tcout(TCBOLDFACEBEG);
+ tsetattrs(TXTBOLDFACE);
break;
case 'b':
- b = 0; m = 1;
if (dopr)
- tcout(TCALLATTRSOFF);
+ tunsetattrs(TXTBOLDFACE);
break;
case 'S':
- s = 1;
if (dopr)
- tcout(TCSTANDOUTBEG);
+ tsetattrs(TXTSTANDOUT);
break;
case 's':
- s = 0; m = 1;
if (dopr)
- tcout(TCSTANDOUTEND);
+ tunsetattrs(TXTSTANDOUT);
break;
case 'U':
- u = 1;
if (dopr)
- tcout(TCUNDERLINEBEG);
+ tsetattrs(TXTUNDERLINE);
break;
case 'u':
- u = 0; m = 1;
if (dopr)
- tcout(TCUNDERLINEEND);
+ tunsetattrs(TXTUNDERLINE);
break;
case 'F':
case 'K':
@@ -2491,18 +2493,27 @@ printfmt(char *fmt, int n, int dopr, int doesc)
} else
atr = match_colour(NULL, is_fg, arg);
if (atr != TXT_ERROR)
- set_colour_attribute(atr, is_fg ? COL_SEQ_FG :
- COL_SEQ_BG, 0);
+ tsetattrs(atr);
break;
case 'f':
- set_colour_attribute(TXTNOFGCOLOUR, COL_SEQ_FG, 0);
+ tunsetattrs(TXTFGCOLOUR);
break;
case 'k':
- set_colour_attribute(TXTNOBGCOLOUR, COL_SEQ_BG, 0);
+ tunsetattrs(TXTBGCOLOUR);
+ break;
+ case 'H':
+ if (p[1] == '{') {
+ p = parsehighlight(p + 2, '}', &atr);
+ --p;
+ if (atr != TXT_ERROR)
+ treplaceattrs(atr);
+ }
break;
case '{':
if (arg)
cc += arg;
+ if (dopr)
+ applytextattributes(0);
for (p++; *p && (*p != '%' || p[1] != '}'); p++) {
if (*p == Meta) {
p++;
@@ -2518,14 +2529,6 @@ printfmt(char *fmt, int n, int dopr, int doesc)
p--;
break;
}
- if (dopr && m) {
- if (b)
- tcout(TCBOLDFACEBEG);
- if (s)
- tcout(TCSTANDOUTBEG);
- if (u)
- tcout(TCUNDERLINEBEG);
- }
} else
break;
p++;
@@ -2533,6 +2536,7 @@ printfmt(char *fmt, int n, int dopr, int doesc)
if (*p == '\n') {
cc++;
if (dopr) {
+ applytextattributes(0);
if (tccan(TCCLEAREOL))
tcout(TCCLEAREOL);
else {
@@ -2551,6 +2555,7 @@ printfmt(char *fmt, int n, int dopr, int doesc)
convchar_t cchar;
int clen = MB_METACHARLENCONV(p, &cchar);
if (dopr) {
+ applytextattributes(0);
while (clen--) {
if (*p == Meta) {
p++;
@@ -2568,6 +2573,8 @@ printfmt(char *fmt, int n, int dopr, int doesc)
}
}
if (dopr) {
+ treplaceattrs(0);
+ applytextattributes(0);
if (!(cc % zterm_columns))
fputs(" \010", shout);
if (tccan(TCCLEAREOL))
diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c
index 526216fa7..e2b86e863 100644
--- a/Src/Zle/zle_utils.c
+++ b/Src/Zle/zle_utils.c
@@ -512,12 +512,13 @@ stringaszleline(char *instr, int incs, int *outll, int *outsz, int *outcs)
*outcs = outptr - outstr;
*outll = outptr - outstr;
} else {
+ *outstr = ZWC('\0');
*outll = 0;
if (outcs)
*outcs = 0;
}
#else
- memcpy(outstr, instr, ll);
+ strcpy(outstr, instr);
*outll = ll;
if (outcs)
*outcs = incs;
@@ -580,7 +581,7 @@ struct zle_region;
struct zle_region {
struct zle_region *next;
/* Entries of region_highlight, as needed */
- int atr;
+ zattr atr;
int start;
int end;
int flags;
@@ -799,7 +800,7 @@ spaceinline(int ct)
if (rhp->start_meta - sub >= zlemetacs) {
rhp->start_meta += ct;
}
- if (rhp->end_meta - sub >= zlemetacs) {
+ if (rhp->end_meta - sub >= zlemetacs && (!predisplaylen || zlemetacs)) {
rhp->end_meta += ct;
}
}
@@ -827,7 +828,7 @@ spaceinline(int ct)
if (rhp->start - sub >= zlecs) {
rhp->start += ct;
}
- if (rhp->end - sub >= zlecs) {
+ if (rhp->end - sub >= zlecs && (!predisplaylen || zlecs)) {
rhp->end += ct;
}
}
@@ -866,13 +867,13 @@ shiftchars(int to, int cnt)
if (rhp->start_meta - sub > to + cnt)
rhp->start_meta -= cnt;
else
- rhp->start_meta = to;
+ rhp->start_meta = to + sub;
}
if (rhp->end_meta - sub > to) {
if (rhp->end_meta - sub > to + cnt)
rhp->end_meta -= cnt;
else
- rhp->end_meta = to;
+ rhp->end_meta = to + sub;
}
}
}
@@ -896,13 +897,13 @@ shiftchars(int to, int cnt)
if (rhp->start - sub > to + cnt)
rhp->start -= cnt;
else
- rhp->start = to;
+ rhp->start = to + sub;
}
if (rhp->end - sub > to) {
if (rhp->end - sub > to + cnt)
rhp->end -= cnt;
else
- rhp->end = to;
+ rhp->end = to + sub;
}
}
}
@@ -936,6 +937,28 @@ cut(int i, int ct, int flags)
cuttext(zleline + i, ct, flags);
}
+static char*
+base64_encode(const char *src, size_t len) {
+ static const char* base64_table =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+ const unsigned char *end = (unsigned char *)src + len;
+ const unsigned char *in = (unsigned char *)src;
+ char *ret = zhalloc(1 + 4 * ((len + 2) / 3)); /* 4 bytes out for 3 in */
+ char *cur = ret;
+
+ for (; end - in > 0; in += 3, cur += 4) {
+ unsigned int n = *in << 16;
+ cur[3] = end - in > 2 ? base64_table[(n |= in[2]) & 0x3f] : '=';
+ cur[2] = end - in > 1 ? base64_table[((n |= in[1]<<8) >> 6) & 0x3f] : '=';
+ cur[1] = base64_table[(n >> 12) & 0x3f];
+ cur[0] = base64_table[n >> 18];
+ }
+ *cur = '\0';
+
+ return ret;
+}
+
/*
* As cut, but explicitly supply the text together with its length.
*/
@@ -948,7 +971,15 @@ cuttext(ZLE_STRING_T line, int ct, int flags)
return;
UNMETACHECK();
- if (zmod.flags & MOD_VIBUF) {
+ if (zmod.flags & MOD_OSSEL) {
+ int cutll;
+ char *mbcut = zlelineasstring(line, ct, 0, &cutll, NULL, 1);
+ unmetafy(mbcut, &cutll);
+ mbcut = base64_encode(mbcut, cutll);
+
+ fprintf(shout, "\033]52;%c;%s\a", zmod.flags & MOD_CLIP ? 'c' : 'p',
+ mbcut);
+ } else if (zmod.flags & MOD_VIBUF) {
struct cutbuffer *b = &vibuf[zmod.vibuf];
if (!(zmod.flags & MOD_VIAPP) || !b->buf) {
@@ -1220,7 +1251,7 @@ getzlequery(void)
REFRESH_ELEMENT re;
re.chr = c;
re.atr = 0;
- zwcputc(&re, NULL);
+ zwcputc(&re);
}
return c == ZWC('y');
}
@@ -1235,7 +1266,7 @@ bindztrdup(char *str)
char *buf, *ptr, *ret;
for(ptr = str; *ptr; ptr++) {
- c = *ptr == Meta ? STOUC(*++ptr) ^ 32 : STOUC(*ptr);
+ c = *ptr == Meta ? (unsigned char) *++ptr ^ 32 : (unsigned char) *ptr;
if(c & 0x80) {
len += 3;
c &= 0x7f;
@@ -1249,7 +1280,7 @@ bindztrdup(char *str)
}
ptr = buf = zalloc(len);
for(; *str; str++) {
- c = *str == Meta ? STOUC(*++str) ^ 32 : STOUC(*str);
+ c = *str == Meta ? (unsigned char) *++str ^ 32 : (unsigned char) *str;
if(c & 0x80) {
*ptr++ = '\\';
*ptr++ = 'M';
diff --git a/Src/Zle/zle_vi.c b/Src/Zle/zle_vi.c
index 0f198d0e8..6692df830 100644
--- a/Src/Zle/zle_vi.c
+++ b/Src/Zle/zle_vi.c
@@ -1014,6 +1014,9 @@ int
visetbuffer(char **args)
{
ZLE_INT_T ch;
+ const ZLE_CHAR_T *match = ZWS("_*+");
+ int registermod[] = { MOD_NULL, MOD_PRI, MOD_CLIP };
+ ZLE_CHAR_T *found;
if (*args) {
ch = **args;
@@ -1022,12 +1025,12 @@ visetbuffer(char **args)
} else {
ch = getfullchar(0);
}
- if (ch == ZWC('_')) {
- zmod.flags |= MOD_NULL;
+ if ((found = ZS_strchr(match, ch))) {
+ zmod.flags |= registermod[found - match];
prefixflag = 1;
return 0;
} else
- zmod.flags &= ~MOD_NULL;
+ zmod.flags &= ~(MOD_NULL | MOD_OSSEL);
if ((ch < ZWC('0') || ch > ZWC('9')) &&
(ch < ZWC('a') || ch > ZWC('z')) &&
(ch < ZWC('A') || ch > ZWC('Z')))
diff --git a/Src/builtin.c b/Src/builtin.c
index 1cef7cce8..5563bdba9 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -55,7 +55,7 @@ static struct builtin builtins[] =
BUILTIN("cd", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 2, BIN_CD, "qsPL", NULL),
BUILTIN("chdir", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 2, BIN_CD, "qsPL", NULL),
BUILTIN("continue", BINF_PSPECIAL, bin_break, 0, 1, BIN_CONTINUE, NULL, NULL),
- BUILTIN("declare", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klmp:%rtuxz", NULL),
+ BUILTIN("declare", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klmnp:%rtuxz", NULL),
BUILTIN("dirs", 0, bin_dirs, 0, -1, 0, "clpv", NULL),
BUILTIN("disable", 0, bin_enable, 0, -1, BIN_DISABLE, "afmprs", NULL),
BUILTIN("disown", 0, bin_fg, 0, -1, BIN_DISOWN, NULL, NULL),
@@ -88,7 +88,7 @@ static struct builtin builtins[] =
BUILTIN("jobs", 0, bin_fg, 0, -1, BIN_JOBS, "dlpZrs", NULL),
BUILTIN("kill", BINF_HANDLES_OPTS, bin_kill, 0, -1, 0, NULL, NULL),
BUILTIN("let", 0, bin_let, 1, -1, 0, NULL, NULL),
- BUILTIN("local", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%ahi:%lp:%rtux", NULL),
+ BUILTIN("local", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%ahi:%lnp:%rtux", NULL),
BUILTIN("logout", 0, bin_break, 0, 1, BIN_LOGOUT, NULL, NULL),
#if defined(ZSH_MEM) & defined(ZSH_MEM_DEBUG)
@@ -121,12 +121,12 @@ static struct builtin builtins[] =
BUILTIN("trap", BINF_PSPECIAL | BINF_HANDLES_OPTS, bin_trap, 0, -1, 0, NULL, NULL),
BUILTIN("true", 0, bin_true, 0, -1, 0, NULL, NULL),
BUILTIN("type", 0, bin_whence, 0, -1, 0, "ampfsSw", "v"),
- BUILTIN("typeset", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klp:%rtuxmz", NULL),
+ BUILTIN("typeset", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klp:%rtuxmnz", NULL),
BUILTIN("umask", 0, bin_umask, 0, 1, 0, "S", NULL),
BUILTIN("unalias", 0, bin_unhash, 0, -1, BIN_UNALIAS, "ams", NULL),
BUILTIN("unfunction", 0, bin_unhash, 1, -1, BIN_UNFUNCTION, "m", "f"),
BUILTIN("unhash", 0, bin_unhash, 1, -1, BIN_UNHASH, "adfms", NULL),
- BUILTIN("unset", BINF_PSPECIAL, bin_unset, 1, -1, BIN_UNSET, "fmv", NULL),
+ BUILTIN("unset", BINF_PSPECIAL, bin_unset, 1, -1, BIN_UNSET, "fmvn", NULL),
BUILTIN("unsetopt", 0, bin_setopt, 0, -1, BIN_UNSETOPT, NULL, NULL),
BUILTIN("wait", 0, bin_fg, 0, -1, BIN_WAIT, NULL, NULL),
BUILTIN("whence", 0, bin_whence, 0, -1, 0, "acmpvfsSwx:", NULL),
@@ -2030,6 +2030,25 @@ typeset_single(char *cname, char *pname, Param pm, int func,
int usepm, tc, keeplocal = 0, newspecial = NS_NONE, readonly, dont_set = 0;
char *subscript;
+ if (pm && (pm->node.flags & PM_NAMEREF) && !((off|on) & PM_NAMEREF) &&
+ (pm->level == locallevel || !(on & PM_LOCAL))) {
+ if ((pm = (Param)resolve_nameref(pm, NULL)))
+ pname = pm->node.nam;
+ if (pm && (pm->node.flags & PM_NAMEREF) &&
+ (on & ~(PM_NAMEREF|PM_LOCAL|PM_READONLY))) {
+ /* Changing type of PM_SPECIAL|PM_AUTOLOAD is a fatal error. *
+ * Should this be a fatal error as well, rather than warning? */
+ if (pm->width)
+ zwarnnam(cname,
+ "%s: can't change type via subscript reference",
+ pm->u.str);
+ else
+ zwarnnam(cname, "%s: can't change type of a named reference",
+ pname);
+ return NULL;
+ }
+ }
+
/*
* Do we use the existing pm? Note that this isn't the end of the
* story, because if we try and create a new pm at the same
@@ -2040,7 +2059,7 @@ typeset_single(char *cname, char *pname, Param pm, int func,
* POSIXBUILTINS horror: we need to retain the 'readonly' or 'export'
* flags of an unset parameter.
*/
- usepm = pm && (!(pm->node.flags & PM_UNSET) ||
+ usepm = pm && (!(pm->node.flags & PM_UNSET) || OPT_ISSET(ops, 'p') ||
(isset(POSIXBUILTINS) &&
(pm->node.flags & (PM_READONLY|PM_EXPORTED))));
@@ -2088,15 +2107,27 @@ typeset_single(char *cname, char *pname, Param pm, int func,
return NULL;
}
tc = 1;
- usepm = 0;
+ if (OPT_MINUS(ops,'p'))
+ usepm = (on & pm->node.flags);
+ else if (OPT_PLUS(ops,'p'))
+ usepm = (off & pm->node.flags);
+ else
+ usepm = 0;
}
else if (usepm || newspecial != NS_NONE) {
int chflags = ((off & pm->node.flags) | (on & ~pm->node.flags)) &
(PM_INTEGER|PM_EFLOAT|PM_FFLOAT|PM_HASHED|
PM_ARRAY|PM_TIED|PM_AUTOLOAD);
/* keep the parameter if just switching between floating types */
- if ((tc = chflags && chflags != (PM_EFLOAT|PM_FFLOAT)))
- usepm = 0;
+ if ((tc = chflags && chflags != (PM_EFLOAT|PM_FFLOAT))) {
+ if (OPT_MINUS(ops,'p'))
+ usepm = (on & pm->node.flags);
+ else if (OPT_PLUS(ops,'p'))
+ usepm = (off & pm->node.flags);
+ else
+ usepm = 0;
+ }
+
}
/*
@@ -2148,20 +2179,23 @@ typeset_single(char *cname, char *pname, Param pm, int func,
}
if (err)
{
- zerrnam(cname, "%s: can't change type of a special parameter",
- pname);
+ if (!OPT_ISSET(ops,'p'))
+ zerrnam(cname,
+ "%s: can't change type of a special parameter",
+ pname);
return NULL;
}
} else if (pm->node.flags & PM_AUTOLOAD) {
- zerrnam(cname, "%s: can't change type of autoloaded parameter",
- pname);
+ if (!OPT_ISSET(ops,'p'))
+ zerrnam(cname, "%s: can't change type of autoloaded parameter",
+ pname);
return NULL;
}
}
else if (newspecial != NS_NONE && strcmp(pname, "SECONDS") == 0)
newspecial = NS_SECONDS;
- if (isset(POSIXBUILTINS)) {
+ if (isset(POSIXBUILTINS) && !OPT_ISSET(ops,'p')) {
/*
* Stricter rules about retaining readonly attribute in this case.
*/
@@ -2190,6 +2224,11 @@ typeset_single(char *cname, char *pname, Param pm, int func,
* ii. we are creating a new local parameter
*/
if (usepm) {
+ if (OPT_MINUS(ops,'p') && on &&
+ !((on & pm->node.flags) || ((on & PM_LOCAL) && pm->level)))
+ return NULL;
+ else if (OPT_PLUS(ops,'p') && off && !(off & pm->node.flags))
+ return NULL;
if ((asg->flags & ASG_ARRAY) ?
!(PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED)) :
(asg->value.scalar && (PM_TYPE(pm->node.flags &
@@ -2199,17 +2238,27 @@ typeset_single(char *cname, char *pname, Param pm, int func,
}
on &= ~PM_LOCAL;
if (!on && !roff && !ASG_VALUEP(asg)) {
+ int with_ns = OPT_ISSET(ops,'m') ? PRINT_WITH_NAMESPACE : 0;
if (OPT_ISSET(ops,'p'))
- paramtab->printnode(&pm->node, PRINT_TYPESET);
+ paramtab->printnode(&pm->node, PRINT_TYPESET|with_ns);
else if (!OPT_ISSET(ops,'g') &&
(unset(TYPESETSILENT) || OPT_ISSET(ops,'m')))
- paramtab->printnode(&pm->node, PRINT_INCLUDEVALUE);
+ paramtab->printnode(&pm->node, PRINT_INCLUDEVALUE|with_ns);
return pm;
}
if ((pm->node.flags & PM_RESTRICTED) && isset(RESTRICTED)) {
zerrnam(cname, "%s: restricted", pname);
return pm;
}
+ if ((pm->node.flags & PM_READONLY) && !(off & PM_READONLY) &&
+ /* It seems as though these checks should not be specific to
+ * PM_NAMEREF, but changing that changes historic behavior */
+ ((on & PM_NAMEREF) != (pm->node.flags & PM_NAMEREF) ||
+ (asg && (pm->node.flags & PM_NAMEREF))) && !OPT_ISSET(ops,'p')) {
+ zerrnam(cname, "%s: read-only %s", pname,
+ (pm->node.flags & PM_NAMEREF) ? "reference" : "variable");
+ return NULL;
+ }
if ((on & PM_UNIQUE) && !(pm->node.flags & PM_READONLY & ~off)) {
Param apm;
char **x;
@@ -2231,6 +2280,10 @@ typeset_single(char *cname, char *pname, Param pm, int func,
arrfixenv(pm->node.nam, x);
}
}
+ if (OPT_ISSET(ops,'p')) {
+ paramtab->printnode(&pm->node, PRINT_TYPESET|PRINT_WITH_NAMESPACE);
+ return pm;
+ }
if (usepm == 2) /* do not change the PM_UNSET flag */
pm->node.flags = (pm->node.flags | (on & ~PM_READONLY)) & ~off;
else {
@@ -2286,10 +2339,9 @@ typeset_single(char *cname, char *pname, Param pm, int func,
if (errflag)
return NULL;
pm->node.flags |= (on & PM_READONLY);
- if (OPT_ISSET(ops,'p'))
- paramtab->printnode(&pm->node, PRINT_TYPESET);
return pm;
- }
+ } else if (OPT_ISSET(ops,'p'))
+ return NULL; /* Nothing to print */
if ((asg->flags & ASG_ARRAY) ?
!(on & (PM_ARRAY|PM_HASHED)) :
@@ -2304,7 +2356,7 @@ typeset_single(char *cname, char *pname, Param pm, int func,
* or we're converting the type of a parameter. In the
* last case only, we need to delete the old parameter.
*/
- if (tc) {
+ if (tc && !OPT_ISSET(ops,'p')) {
/* Maintain existing readonly/exported status... */
on |= ~off & (PM_READONLY|PM_EXPORTED) & pm->node.flags;
/* ...but turn off existing readonly so we can delete it */
@@ -2406,6 +2458,11 @@ typeset_single(char *cname, char *pname, Param pm, int func,
return NULL;
}
} else if ((subscript = strchr(pname, '['))) {
+ if (on & PM_NAMEREF) {
+ zerrnam(cname,
+ "%s: reference variable cannot be an array", pname);
+ return NULL;
+ }
if (on & PM_READONLY) {
zerrnam(cname,
"%s: can't create readonly array elements", pname);
@@ -2452,6 +2509,8 @@ typeset_single(char *cname, char *pname, Param pm, int func,
"%s: inconsistent array element or slice assignment", pname);
return NULL;
}
+ } else if (!pm && OPT_ISSET(ops,'p')) {
+ return NULL;
}
/*
* As we can hide existing parameters, we allow a name if
@@ -2613,7 +2672,7 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
char *optstr = TYPESET_OPTSTR;
int on = 0, off = 0, roff, bit = PM_ARRAY;
int i;
- int returnval = 0, printflags = 0;
+ int returnval = 0, printflags = PRINT_WITH_NAMESPACE;
int hasargs = *argv != NULL || (assigns && firstnode(assigns));
/* POSIXBUILTINS is set for bash/ksh and both ignore -p with args */
@@ -2629,17 +2688,42 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
if (func == BIN_READONLY && isset(POSIXBUILTINS) && !OPT_PLUS(ops, 'g'))
ops->ind['g'] = 1;
+#if 0
+ /* "local" rejects -m, this should too ... what about +m ? */
+ if (locallevel && OPT_MINUS(ops, 'm') &&
+ !(OPT_MINUS(ops, 'g') || OPT_ISSET(ops, 'p'))) {
+ zerrnam(name, "bad option: -m");
+ return 1;
+ }
+#endif
+
/* Translate the options into PM_* flags. *
* Unfortunately, this depends on the order *
* these flags are defined in zsh.h */
for (; *optstr; optstr++, bit <<= 1)
{
- int optval = STOUC(*optstr);
+ int optval = (unsigned char) *optstr;
if (OPT_MINUS(ops,optval))
on |= bit;
else if (OPT_PLUS(ops,optval))
off |= bit;
+ else
+ continue;
+ if (OPT_MINUS(ops,'n')) {
+ if ((on|off) & ~(PM_READONLY|PM_UPPER|PM_HIDEVAL)) {
+ zwarnnam(name, "-%c not allowed with -n", optval);
+ /* return 1; */
+ }
+ }
}
+ if (OPT_MINUS(ops,'n')) {
+ if ((on|off) & ~(PM_READONLY|PM_UPPER|PM_HIDEVAL)) {
+ /* zwarnnam(name, "no other attributes allowed with -n"); */
+ return 1;
+ }
+ on |= PM_NAMEREF;
+ } else if (OPT_PLUS(ops,'n'))
+ off |= PM_NAMEREF;
roff = off;
/* Sanity checks on the options. Remove conflicting options. */
@@ -2673,7 +2757,6 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
queue_signals();
- /* Given no arguments, list whatever the options specify. */
if (OPT_ISSET(ops,'p')) {
if (isset(POSIXBUILTINS) && SHELL_EMULATION() != EMULATE_KSH) {
@@ -2699,8 +2782,14 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
/* -p0 treated as -p for consistency */
}
}
+
+ /* Given no arguments, list whatever the options specify. */
if (!hasargs) {
int exclude = 0;
+
+ if (!OPT_ISSET(ops,'m'))
+ printflags &= ~PRINT_WITH_NAMESPACE;
+
if (!OPT_ISSET(ops,'p')) {
if (!(on|roff))
printflags |= PRINT_TYPE;
@@ -2718,10 +2807,18 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
return 0;
}
- if (!(OPT_ISSET(ops,'g') || OPT_ISSET(ops,'x') || OPT_ISSET(ops,'m')) ||
- OPT_PLUS(ops,'g') || *name == 'l' ||
- (!isset(GLOBALEXPORT) && !OPT_ISSET(ops,'g')))
- on |= PM_LOCAL;
+ /* Using *name here is cheating, "local" allows no -g option */
+ if ((*name == 'l' || OPT_PLUS(ops,'g')))
+ on |= PM_LOCAL;
+ else if (!OPT_ISSET(ops,'g')) {
+ if (OPT_MINUS(ops, 'x')) {
+ if (isset(GLOBALEXPORT))
+ ops->ind['g'] = 1;
+ else if (locallevel)
+ on |= PM_LOCAL;
+ } else if (!(OPT_ISSET(ops,'x') || OPT_ISSET(ops,'m')))
+ on |= PM_LOCAL;
+ }
if ((on & PM_TIED) && !OPT_ISSET(ops, 'p')) {
Param apm;
@@ -2956,6 +3053,13 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
/* With the -m option, treat arguments as glob patterns */
if (OPT_ISSET(ops,'m')) {
if (!OPT_ISSET(ops,'p')) {
+ if ((on & PM_NAMEREF) && OPT_MINUS(ops,'m')) {
+ /* It's generally unwise to mass-change the types of
+ * parameters, but for namerefs it would be fatal */
+ unqueue_signals();
+ zerrnam(name, "-m not allowed with -n");
+ return 1;
+ }
if (!(on|roff))
printflags |= PRINT_TYPE;
if (!on)
@@ -3022,6 +3126,47 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
}
continue;
}
+
+ if (on & PM_NAMEREF) {
+ if (asg->value.scalar &&
+ ((pm = (Param)resolve_nameref((Param)hn, asg)) &&
+ (pm->node.flags & PM_NAMEREF))) {
+ if (pm->node.flags & PM_SPECIAL) {
+ zwarnnam(name, "%s: invalid reference", pm->node.nam);
+ returnval = 1;
+ continue;
+ } else if (pm->u.str && strcmp(pm->u.str, asg->name) == 0) {
+ zwarnnam(name, "%s: invalid self reference", asg->name);
+ returnval = 1;
+ continue;
+ }
+ }
+ if (hn) {
+ /* namerefs always start over fresh */
+ if (((Param)hn)->level >= locallevel ||
+ (!(on & PM_LOCAL) && ((Param)hn)->level < locallevel)) {
+ Param oldpm = (Param)hn;
+ if (!asg->value.scalar &&
+ PM_TYPE(oldpm->node.flags) == PM_SCALAR &&
+ oldpm->u.str)
+ asg->value.scalar = dupstring(oldpm->u.str);
+ /* Defer read-only error to typeset_single() */
+ if (!(hn->flags & PM_READONLY)) {
+ unsetparam_pm(oldpm, 0, 1);
+ hn = NULL;
+ }
+ }
+ /* Passing a NULL pm to typeset_single() makes the
+ * nameref read-only before assignment, which breaks
+ * typeset -rn ref=var
+ * so this is special-cased to permit that action
+ * like assign-at-create for other parameter types.
+ */
+ if (hn && !(hn->flags & PM_READONLY))
+ hn = NULL;
+ }
+ }
+
if (!typeset_single(name, asg->name, (Param)hn,
func, on, off, roff, asg, NULL,
ops, 0))
@@ -3274,6 +3419,7 @@ bin_functions(char *name, char **argv, Options ops, int func)
if (OPT_ISSET(ops,'c')) {
Shfunc newsh;
+ char *s = argv[1];
if (!*argv || !argv[1] || argv[2]) {
zwarnnam(name, "-c: requires two arguments");
return 1;
@@ -3304,8 +3450,22 @@ bin_functions(char *name, char **argv, Options ops, int func)
if (newsh->redir)
newsh->redir->nref++;
if (shf->sticky)
- newsh->sticky = sticky_emulation_dup(sticky, 0);
- shfunctab->addnode(shfunctab, ztrdup(argv[1]), &newsh->node);
+ newsh->sticky = sticky_emulation_dup(shf->sticky, 0);
+ /* is newsh a signal trap? (adapted from exec.c) */
+ if (!strncmp(s, "TRAP", 4)) {
+ int sigidx = getsigidx(s + 4);
+ if (sigidx != -1) {
+ if (settrap(sigidx, NULL, ZSIG_FUNC)) {
+ freeeprog(newsh->funcdef);
+ dircache_set(&newsh->filename, NULL);
+ zfree(newsh, sizeof(*newsh));
+ return 1;
+ }
+ /* Remove any old node explicitly */
+ removetrapnode(sigidx);
+ }
+ }
+ shfunctab->addnode(shfunctab, ztrdup(s), &newsh->node);
return 0;
}
@@ -3581,15 +3741,15 @@ bin_functions(char *name, char **argv, Options ops, int func)
/* no flags, so just print */
printshfuncexpand(&shf->node, pflags, expand);
} else if (on & PM_UNDEFINED) {
- int signum = -1, ok = 1;
+ int sigidx = -1, ok = 1;
if (!strncmp(*argv, "TRAP", 4) &&
- (signum = getsignum(*argv + 4)) != -1) {
+ (sigidx = getsigidx(*argv + 4)) != -1) {
/*
* Because of the possibility of alternative names,
* we must remove the trap explicitly.
*/
- removetrapnode(signum);
+ removetrapnode(sigidx);
}
if (**argv == '/') {
@@ -3625,8 +3785,8 @@ bin_functions(char *name, char **argv, Options ops, int func)
shfunc_set_sticky(shf);
add_autoload_function(shf, *argv);
- if (signum != -1) {
- if (settrap(signum, NULL, ZSIG_FUNC)) {
+ if (sigidx != -1) {
+ if (settrap(sigidx, NULL, ZSIG_FUNC)) {
shfunctab->removenode(shfunctab, *argv);
shfunctab->freenode(&shf->node);
returnval = 1;
@@ -3700,7 +3860,11 @@ bin_unset(char *name, char **argv, Options ops, int func)
if ((!(pm->node.flags & PM_RESTRICTED) ||
unset(RESTRICTED)) &&
pattry(pprog, pm->node.nam)) {
- unsetparam_pm(pm, 0, 1);
+ if (!OPT_ISSET(ops,'n') &&
+ (pm->node.flags & PM_NAMEREF) && pm->u.str)
+ unsetparam(pm->u.str);
+ else
+ unsetparam_pm(pm, 0, 1);
match++;
}
}
@@ -3752,6 +3916,11 @@ bin_unset(char *name, char **argv, Options ops, int func)
zerrnam(name, "%s: restricted", pm->node.nam);
returnval = 1;
} else if (ss) {
+ if ((pm->node.flags & PM_NAMEREF) &&
+ (!(pm = (Param)resolve_nameref(pm, NULL)) || pm->width)) {
+ /* warning? */
+ continue;
+ }
if (PM_TYPE(pm->node.flags) == PM_HASHED) {
HashTable tht = paramtab;
if ((paramtab = pm->gsu.h->getfn(pm)))
@@ -3790,6 +3959,18 @@ bin_unset(char *name, char **argv, Options ops, int func)
returnval = 1;
}
} else {
+ if (!OPT_ISSET(ops,'n')) {
+ int ref = (pm->node.flags & PM_NAMEREF);
+ if (!(pm = (Param)resolve_nameref(pm, NULL)))
+ continue;
+ if (ref && pm->level < locallevel &&
+ !(pm->node.flags & PM_READONLY)) {
+ /* Just mark unset, do not remove from table */
+ stdunsetfn(pm, 0);
+ pm->node.flags |= PM_DECLARED;
+ continue;
+ }
+ }
if (unsetparam_pm(pm, 0, 1))
returnval = 1;
}
@@ -3926,6 +4107,7 @@ bin_whence(char *nam, char **argv, Options ops, int func)
/* Take arguments literally -- do not glob */
queue_signals();
for (; *argv; argv++) {
+ informed = 0;
if (!OPT_ISSET(ops,'p') && !allmatched) {
char *suf;
@@ -4588,6 +4770,8 @@ bin_print(char *name, char **args, Options ops, int func)
/* compute lengths, and interpret according to -P, -D, -e, etc. */
argc = arrlen(args);
len = (int *) hcalloc(argc * sizeof(int));
+ if (OPT_ISSET(ops, 'P'))
+ txtunknownattrs = TXT_ATTR_ALL;
for (n = 0; n < argc; n++) {
/* first \ sequences */
if (fmt ||
@@ -4618,7 +4802,7 @@ bin_print(char *name, char **args, Options ops, int func)
*/
char *str = unmetafy(
promptexpand(metafy(args[n], len[n], META_NOALLOC),
- 0, NULL, NULL, NULL),
+ 0, NULL, NULL),
&len[n]);
args[n] = dupstrpfx(str, len[n]);
free(str);
@@ -4633,9 +4817,8 @@ bin_print(char *name, char **args, Options ops, int func)
if (d) {
int dirlen = strlen(d->dir);
char *arg = zhalloc(len[n] - dirlen + strlen(d->node.nam) + 2);
- sprintf(arg, "~%s%s", d->node.nam, args[n] + dirlen);
+ len[n] = sprintf(arg, "~%s%s", d->node.nam, args[n] + dirlen);
args[n] = arg;
- len[n] = strlen(args[n]);
}
unqueue_signals();
}
@@ -4737,7 +4920,7 @@ bin_print(char *name, char **args, Options ops, int func)
*/
if (*aptr == '\033' || *aptr == '\233') {
for (aptr++, l--;
- l && !isalpha(STOUC(*aptr));
+ l && !isalpha((unsigned char) (*aptr));
aptr++, l--)
;
aptr++;
@@ -5099,7 +5282,8 @@ bin_print(char *name, char **args, Options ops, int func)
}
}
if (*argp) {
- width = (int)mathevali(*argp++);
+ width = (int)mathevali(metafy(*argp, len[argp - args], META_NOALLOC));
+ argp++;
if (errflag) {
errflag &= ~ERRFLAG_ERROR;
ret = 1;
@@ -5133,7 +5317,8 @@ bin_print(char *name, char **args, Options ops, int func)
}
if (*argp) {
- prec = (int)mathevali(*argp++);
+ prec = (int)mathevali(metafy(*argp, len[argp - args], META_NOALLOC));
+ argp++;
if (errflag) {
errflag &= ~ERRFLAG_ERROR;
ret = 1;
@@ -5207,20 +5392,21 @@ bin_print(char *name, char **args, Options ops, int func)
#ifdef MULTIBYTE_SUPPORT
if (isset(MULTIBYTE)) {
chars = mbrlen(ptr, lleft, &mbs);
- if (chars < 0) {
- /*
- * Invalid/incomplete character at this
- * point. Assume all the rest are a
- * single byte. That's about the best we
- * can do.
- */
- lchars += lleft;
- lbytes = (ptr - b) + lleft;
- break;
- } else if (chars == 0) {
- /* NUL, handle as real character */
+ /*
+ * chars <= 0 means one of
+ *
+ * 0: NUL, handle as real character
+ *
+ * -1: MB_INVALID: Assume this is
+ * a single character as we do
+ * elsewhere in the code.
+ *
+ * -2: MB_INCOMPLETE: We're not waiting
+ * for input on this occasion, so
+ * just treat this as invalid.
+ */
+ if (chars <= 0)
chars = 1;
- }
}
else /* use the non-multibyte code below */
#endif
@@ -5244,7 +5430,7 @@ bin_print(char *name, char **args, Options ops, int func)
break;
case 'q':
stringval = curarg ?
- quotestring(metafy(curarg, curlen, META_USEHEAP),
+ quotestring(metafy(curarg, curlen, META_NOALLOC),
QT_BACKSLASH_SHOWNULL) : &nullstr;
*d = 's';
print_val(unmetafy(stringval, &curlen));
@@ -5276,9 +5462,8 @@ bin_print(char *name, char **args, Options ops, int func)
}
zwarnnam(name, "%s: invalid directive", start);
if (*c) c[1] = save;
- /* Why do we care about a clean close here? */
- if (!CLOSE_CLEANLY(fout))
- zwarnnam(name, "write error: %e", errno);
+ if (fout != stdout)
+ fclose(fout);
#ifdef HAVE_OPEN_MEMSTREAM
if (buf)
free(buf);
@@ -5298,9 +5483,9 @@ bin_print(char *name, char **args, Options ops, int func)
else
cc = WEOF;
if (cc == WEOF)
- cc = (curlen > 1) ? STOUC(curarg[1]) : 0;
+ cc = (curlen > 1) ? (unsigned char) (curarg[1]) : 0;
#else
- cc = (curlen > 1) ? STOUC(curarg[1]) : 0;
+ cc = (curlen > 1) ? (unsigned char) (curarg[1]) : 0;
#endif
if (type == 2) {
doubleval = cc;
@@ -5316,7 +5501,7 @@ bin_print(char *name, char **args, Options ops, int func)
*d++ = 'l';
#endif
*d++ = 'l', *d++ = *c, *d = '\0';
- zlongval = (curarg) ? mathevali(curarg) : 0;
+ zlongval = (curarg) ? mathevali(metafy(curarg, curlen, META_NOALLOC)) : 0;
if (errflag) {
zlongval = 0;
errflag &= ~ERRFLAG_ERROR;
@@ -5367,7 +5552,7 @@ bin_print(char *name, char **args, Options ops, int func)
if (!curarg)
zulongval = (zulong)0;
else if (!zstrtoul_underscore(curarg, &zulongval))
- zulongval = mathevali(curarg);
+ zulongval = mathevali(metafy(curarg, curlen, META_NOALLOC));
if (errflag) {
zulongval = 0;
errflag &= ~ERRFLAG_ERROR;
@@ -6148,7 +6333,7 @@ bin_emulate(char *nam, char **argv, Options ops, UNUSED(int func))
savehackchar = keyboardhackchar;
emulate(shname, opt_R, &new_emulation, new_opts);
optlist = newlinklist();
- if (parseopts(nam, &argv, new_opts, &cmd, optlist, 0, NULL)) {
+ if (parseopts(nam, &argv, new_opts, &cmd, optlist, 0)) {
ret = 1;
goto restore;
}
@@ -6267,11 +6452,12 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func))
long izle_timeout = 0;
#ifdef MULTIBYTE_SUPPORT
wchar_t delim = L'\n', wc;
+ int rawbyte = 0;
mbstate_t mbs;
char *laststart;
size_t ret;
#else
- char delim = '\n';
+ int delim = '\n';
#endif
if (OPT_HASARG(ops,c='k')) {
@@ -6358,9 +6544,10 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func))
} else
readfd = izle = 0;
- if (OPT_ISSET(ops,'s') && SHTTY != -1) {
+ if (OPT_ISSET(ops,'s') && isatty(readfd)) {
struct ttyinfo ti;
- gettyinfo(&ti);
+ memset(&ti, 0, sizeof(struct ttyinfo));
+ fdgettyinfo(readfd, &ti);
saveti = ti;
resettty = 1;
#ifdef HAS_TIO
@@ -6368,7 +6555,7 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func))
#else
ti.sgttyb.sg_flags &= ~ECHO;
#endif
- settyinfo(&ti);
+ fdsettyinfo(readfd, &ti);
}
/* handle prompt */
@@ -6397,15 +6584,18 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func))
wi = WEOF;
if (wi != WEOF)
delim = (wchar_t)wi;
- else
- delim = (wchar_t)((delimstr[0] == Meta) ?
+ else {
+ delim = (wchar_t) (unsigned char) ((delimstr[0] == Meta) ?
delimstr[1] ^ 32 : delimstr[0]);
+ rawbyte = 1;
+ }
#else
- delim = (delimstr[0] == Meta) ? delimstr[1] ^ 32 : delimstr[0];
+ delim = (unsigned char) ((delimstr[0] == Meta) ?
+ delimstr[1] ^ 32 : delimstr[0]);
#endif
- if (SHTTY != -1) {
+ if (isatty(readfd)) {
struct ttyinfo ti;
- gettyinfo(&ti);
+ fdgettyinfo(readfd, &ti);
if (! resettty) {
saveti = ti;
resettty = 1;
@@ -6417,7 +6607,7 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func))
#else
ti.sgttyb.sg_flags |= CBREAK;
#endif
- settyinfo(&ti);
+ fdsettyinfo(readfd, &ti);
}
}
if (OPT_ISSET(ops,'t')) {
@@ -6452,10 +6642,11 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func))
timeout)) {
if (keys && !zleactive && !isem)
settyinfo(&shttyinfo);
- else if (resettty && SHTTY != -1)
- settyinfo(&saveti);
+ else if (resettty)
+ fdsettyinfo(readfd, &saveti);
if (haso) {
- fclose(shout);
+ if (shout)
+ fclose(shout);
shout = oshout;
SHTTY = -1;
}
@@ -6563,8 +6754,8 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func))
/* dispose of result appropriately, etc. */
if (isem)
while (val > 0 && read(SHTTY, &d, 1) == 1 && d != '\n');
- else {
- settyinfo(&shttyinfo);
+ else if (resettty) {
+ fdsettyinfo(readfd, &saveti);
resettty = 0;
}
if (haso) {
@@ -6593,8 +6784,8 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func))
setsparam(reply, metafy(buf, bptr - buf, META_REALLOC));
else
zfree(buf, bptr - buf + 1);
- if (resettty && SHTTY != -1)
- settyinfo(&saveti);
+ if (resettty)
+ fdsettyinfo(readfd, &saveti);
return eof;
}
@@ -6670,7 +6861,7 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func))
continue;
first = 0;
}
- if (imeta(STOUC(*bptr))) {
+ if (imeta((unsigned char) *bptr)) {
bptr[1] = bptr[0] ^ 32;
bptr[0] = Meta;
bptr += 2;
@@ -6804,8 +6995,8 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func))
*pp++ = NULL;
setaparam(reply, p);
}
- if (resettty && SHTTY != -1)
- settyinfo(&saveti);
+ if (resettty)
+ fdsettyinfo(readfd, &saveti);
return c == EOF;
}
buf = bptr = (char *)zalloc(bsiz = 64);
@@ -6826,7 +7017,7 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func))
break;
}
*bptr = (char)c;
- if (isset(MULTIBYTE)) {
+ if (isset(MULTIBYTE) && !rawbyte) {
ret = mbrtowc(&wc, bptr, 1, &mbs);
if (!ret) /* NULL */
ret = 1;
@@ -6863,7 +7054,7 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func))
if (bslash)
continue;
}
- if (imeta(STOUC(*bptr))) {
+ if (imeta((unsigned char) *bptr)) {
bptr[1] = bptr[0] ^ 32;
bptr[0] = Meta;
bptr += 2;
@@ -6933,8 +7124,8 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func))
break;
}
*bptr = '\0';
- if (resettty && SHTTY != -1)
- settyinfo(&saveti);
+ if (resettty)
+ fdsettyinfo(readfd, &saveti);
/* final assignment of reply, etc. */
if (OPT_ISSET(ops,'e') || OPT_ISSET(ops,'E')) {
zputs(buf, stdout);
@@ -6985,14 +7176,14 @@ zread(int izle, int *readchar, long izle_timeout)
buffer. This may be a null byte to indicate EOF. If reading from the
buffer, move on the buffer pointer. */
if (*zbuf == Meta)
- return zbuf++, STOUC(*zbuf++ ^ 32);
+ return zbuf++, (unsigned char) (*zbuf++ ^ 32);
else
- return (*zbuf) ? STOUC(*zbuf++) : EOF;
+ return (*zbuf) ? (unsigned char) *zbuf++ : EOF;
}
if (*readchar >= 0) {
cc = *readchar;
*readchar = -1;
- return STOUC(cc);
+ return (unsigned char) cc;
}
for (;;) {
/* read a character from readfd */
@@ -7000,7 +7191,7 @@ zread(int izle, int *readchar, long izle_timeout)
switch (ret) {
case 1:
/* return the character read */
- return STOUC(cc);
+ return (unsigned char) cc;
case -1:
#if defined(EAGAIN) || defined(EWOULDBLOCK)
if (!retry && readfd == 0 && (
@@ -7189,7 +7380,7 @@ bin_trap(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
/* If given no arguments, list all currently-set traps */
if (!*argv) {
queue_signals();
- for (sig = 0; sig < VSIGCOUNT; sig++) {
+ for (sig = 0; sig < TRAPCOUNT; sig++) {
if (sigtrapped[sig] & ZSIG_FUNC) {
HashNode hn;
@@ -7215,13 +7406,13 @@ bin_trap(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
/* If we have a signal number, unset the specified *
* signals. With only -, remove all traps. */
- if ((getsignum(*argv) != -1) || (!strcmp(*argv, "-") && argv++)) {
+ if ((getsigidx(*argv) != -1) || (!strcmp(*argv, "-") && argv++)) {
if (!*argv) {
- for (sig = 0; sig < VSIGCOUNT; sig++)
+ for (sig = 0; sig < TRAPCOUNT; sig++)
unsettrap(sig);
} else {
for (; *argv; argv++) {
- sig = getsignum(*argv);
+ sig = getsigidx(*argv);
if (sig == -1) {
zwarnnam(name, "undefined signal: %s", *argv);
break;
@@ -7246,12 +7437,12 @@ bin_trap(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
Eprog t;
int flags;
- sig = getsignum(*argv);
+ sig = getsigidx(*argv);
if (sig == -1) {
zwarnnam(name, "undefined signal: %s", *argv);
break;
}
- if (idigit(**argv) ||
+ if (idigit(**argv) || (sig >= VSIGCOUNT) ||
!strcmp(sigs[sig], *argv) ||
(!strncmp("SIG", *argv, 3) && !strcmp(sigs[sig], *argv+3))) {
/* The signal was specified by number or by canonical name (with
diff --git a/Src/compat.c b/Src/compat.c
index 817bb4aaf..918d98e69 100644
--- a/Src/compat.c
+++ b/Src/compat.c
@@ -136,7 +136,19 @@ zgettime_monotonic_if_available(struct timespec *ts)
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
struct timespec dts;
+
+/*
+ * On at least some versions of macOS it appears that CLOCK_MONOTONIC is not
+ * actually monotonic -- there are reports that it can go backwards.
+ * CLOCK_MONOTONIC_RAW does not have this problem. On top of that, it is faster
+ * to read and it has nanosecond precision. We could use it on other systems
+ * too, but on Linux at least it seems that CLOCK_MONOTONIC is preferred
+ */
+#if defined(__APPLE__) && defined(CLOCK_MONOTONIC_RAW)
+ if (clock_gettime(CLOCK_MONOTONIC_RAW, &dts) < 0) {
+#else
if (clock_gettime(CLOCK_MONOTONIC, &dts) < 0) {
+#endif
zwarn("unable to retrieve CLOCK_MONOTONIC time: %e", errno);
ret--;
} else {
@@ -342,36 +354,69 @@ zopenmax(void)
mod_export char *
zgetdir(struct dirsav *d)
{
- char nbuf[PATH_MAX+3];
char *buf;
+#if defined(HAVE_GETCWD) || defined(__CYGWIN__)
+ char *cwdbuf;
+#endif
+#if defined(USE_GETCWD) || defined(__CYGWIN__)
+#ifdef GETCWD_CALLS_MALLOC
+ if ((cwdbuf = getcwd(NULL, 0))) {
+ buf = dupstring(cwdbuf);
+ free(cwdbuf);
+ } else
+ buf = NULL;
+#else
+ cwdbuf = zalloc(PATH_MAX+1);
+ if ((buf = getcwd(cwdbuf, PATH_MAX)))
+ buf = dupstring(buf);
+ zfree(cwdbuf, PATH_MAX+1);
+#endif /* GETCWD_CALLS_MALLOC */
+ if (d && buf)
+ return d->dirname = ztrdup(buf);
+ else
+ return buf;
+#else /* !USE_GETCWD && !__CYGWIN__ */
int bufsiz, pos;
- struct stat sbuf;
- ino_t pino;
- dev_t pdev;
-#if !defined(__CYGWIN__) && !defined(USE_GETCWD)
+ char nbuf[PATH_MAX+3];
struct dirent *de;
DIR *dir;
- dev_t dev;
- ino_t ino;
+ struct stat sbuf;
+ ino_t pino, ino;
+ dev_t pdev, dev;
int len;
-#endif
+ strcpy(nbuf, "../");
+ if (stat(".", &sbuf) == 0) {
+ /* Record the initial inode and device */
+ pino = sbuf.st_ino;
+ pdev = sbuf.st_dev;
+ if (d)
+ d->ino = pino, d->dev = pdev;
+ }
+#ifdef HAVE_GETCWD
+ else {
+#ifdef GETCWD_CALLS_MALLOC
+ if ((cwdbuf = getcwd(NULL, 0))) {
+ buf = dupstring(cwdbuf);
+ free(cwdbuf);
+ } else
+ buf = NULL;
+#else
+ cwdbuf = zalloc(PATH_MAX+1);
+ if ((buf = getcwd(cwdbuf, PATH_MAX)))
+ buf = dupstring(buf);
+ zfree(cwdbuf, PATH_MAX+1);
+#endif /* GETCWD_CALLS_MALLOC */
+ return buf; /* NULL when stat() and getcwd() both failed */
+ }
+#endif
+ /* stat() succeeded */
buf = zhalloc(bufsiz = PATH_MAX+1);
pos = bufsiz - 1;
buf[pos] = '\0';
- strcpy(nbuf, "../");
- if (stat(".", &sbuf) < 0) {
- return NULL;
- }
- /* Record the initial inode and device */
- pino = sbuf.st_ino;
- pdev = sbuf.st_dev;
- if (d)
- d->ino = pino, d->dev = pdev;
-#if !defined(__CYGWIN__) && !defined(USE_GETCWD)
#ifdef HAVE_FCHDIR
- else
+ if (!d)
#endif
holdintr();
@@ -487,18 +532,6 @@ zgetdir(struct dirsav *d)
zchdir(buf + pos + 1);
noholdintr();
-#else /* __CYGWIN__, USE_GETCWD cases */
-
- if (!getcwd(buf, bufsiz)) {
- if (d) {
- return NULL;
- }
- } else {
- if (d) {
- return d->dirname = ztrdup(buf);
- }
- return buf;
- }
#endif
/*
@@ -526,23 +559,6 @@ mod_export char *
zgetcwd(void)
{
char *ret = zgetdir(NULL);
-#ifdef HAVE_GETCWD
- if (!ret) {
-#ifdef GETCWD_CALLS_MALLOC
- char *cwd = getcwd(NULL, 0);
- if (cwd) {
- ret = dupstring(cwd);
- free(cwd);
- }
-#else
- char *cwdbuf = zalloc(PATH_MAX+1);
- ret = getcwd(cwdbuf, PATH_MAX);
- if (ret)
- ret = dupstring(ret);
- zfree(cwdbuf, PATH_MAX+1);
-#endif /* GETCWD_CALLS_MALLOC */
- }
-#endif /* HAVE_GETCWD */
if (!ret)
ret = unmeta(pwd);
if (!ret || *ret == '\0')
diff --git a/Src/exec.c b/Src/exec.c
index f2911807c..c1181c5eb 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -56,14 +56,56 @@ struct funcsave {
typedef struct funcsave *Funcsave;
/*
- * used to suppress ERREXIT and trapping of SIGZERR, SIGEXIT.
+ * Used to suppress ERREXIT and trapping of SIGZERR, SIGEXIT in the
+ * evaluation of sub-commands of the command under evaluation. The
+ * variable must be updated before the evaluation of the sub-commands
+ * starts and restored to its previous state right after that
+ * evaluation ends. The variable is read and acted upon in execlist.
+ *
+ * A good usage example can be found in execwhile in loop.c, which
+ * evaluates while statements. The variable is updated to disable
+ * ERREXIT just before evaluating the while's condition and restored
+ * to its previous state right after the evaluation of the condition.
+ *
* Bits from noerrexit_bits.
*/
/**/
int noerrexit;
-/* used to suppress ERREXIT or ERRRETURN for one occurrence: 0 or 1 */
+/*
+ * Used to suppress ERREXIT and ERRRETURN for the command under
+ * evaluation. The variable must be enabled (set to 1) at the very
+ * end of the evaluation of the command. It must come after the
+ * evaluation of any sub-commands of the command under evaluation. The
+ * variable is read and acted upon in execlist, which also takes care
+ * of initialising and resetting it to 0.
+ *
+ * Unlike the variable noerrexit, whose state applies to the
+ * evaluation of whole sub-commands (and their direct and indirect
+ * sub-commands), the scope of the variable this_noerrexit is much
+ * more localized. ERREXIT and ERRRETURN are triggered at the end of
+ * the function execlist after the evaluation of some or all of the
+ * list's sub-commands. The role of the variable this_noerrexit is to
+ * give to the functions evaluating the list's sub-commands the
+ * possibility to tell the calling execlist not to trigger ERREXIT and
+ * ERRRETURN. In other words, the variable acts as an additional
+ * return value between the called evaluation functions and the
+ * calling execlist. For that reason the variable must always be set
+ * as late as possible and in particular after any sub-command
+ * evaluation. If the variable is set before the evaluation of a
+ * sub-command, if may affect the wrong execlist, if the sub-command
+ * evaluation involves another execlist call, and/or the variable may
+ * get modified by the sub-command evaluation and thus wouldn't return
+ * the desired value to the calling execlist.
+ *
+ * Good usage examples can be found in the exec functions in loop.c,
+ * which evaluate compound commands. The variable is enabled right
+ * before returning from the functions, after all the sub-commands of
+ * the compound commands have already been evaluated.
+ *
+ * 0 or 1
+ */
/**/
int this_noerrexit;
@@ -225,7 +267,7 @@ static char *blank_env[] = { NULL };
/* Execution functions. */
-static int (*execfuncs[WC_COUNT-WC_CURSH]) _((Estate, int)) = {
+static int (*execfuncs[WC_COUNT-WC_CURSH]) (Estate, int) = {
execcursh, exectime, NULL /* execfuncdef handled specially */,
execfor, execselect,
execwhile, execrepeat, execcase, execif, execcond,
@@ -306,10 +348,9 @@ setlimits(char *nam)
/**/
static pid_t
-zfork(struct timeval *tv)
+zfork(struct timespec *ts)
{
pid_t pid;
- struct timezone dummy_tz;
/*
* Is anybody willing to explain this test?
@@ -318,8 +359,8 @@ zfork(struct timeval *tv)
zerr("job table full");
return -1;
}
- if (tv)
- gettimeofday(tv, &dummy_tz);
+ if (ts)
+ zgettime_monotonic_if_available(ts);
/*
* Queueing signals is necessary on Linux because fork()
* manipulates mutexes, leading to deadlock in memory
@@ -418,7 +459,7 @@ zfork(struct timeval *tv)
int list_pipe = 0, simple_pline = 0;
static pid_t list_pipe_pid;
-static struct timeval list_pipe_start;
+static struct timespec list_pipe_start;
static int nowait, pline_level = 0;
static int list_pipe_child = 0, list_pipe_job;
static char list_pipe_text[JOBTEXTSIZE];
@@ -477,7 +518,7 @@ zexecve(char *pth, char **argv, char **newenvp)
if (*pth == '/')
strcpy(buf + 2, pth);
else
- sprintf(buf + 2, "%s/%s", pwd, pth);
+ sprintf(buf + 2, "%s/%s", unmeta(pwd), pth);
zputenv(buf);
#ifndef FD_CLOEXEC
closedumps();
@@ -509,7 +550,7 @@ zexecve(char *pth, char **argv, char **newenvp)
break;
if (t0 == ct)
zerr("%s: bad interpreter: %s: %e", pth,
- execvebuf + 2, eno);
+ metafy(execvebuf + 2, -1, META_STATIC), eno);
else {
while (inblank(execvebuf[t0]))
execvebuf[t0--] = '\0';
@@ -532,8 +573,8 @@ zexecve(char *pth, char **argv, char **newenvp)
execve(pprog, argv - 2, newenvp);
}
}
- zerr("%s: bad interpreter: %s: %e", pth, ptr2,
- eno);
+ zerr("%s: bad interpreter: %s: %e", pth,
+ metafy(ptr2, -1, META_STATIC), eno);
} else if (*ptr) {
*ptr = '\0';
argv[-2] = ptr2;
@@ -561,7 +602,7 @@ zexecve(char *pth, char **argv, char **newenvp)
isbinary = 1;
hasletter = 0;
for (ptr = execvebuf; ptr < ptr2; ptr++) {
- if (islower(STOUC(*ptr)) || *ptr == '$' || *ptr == '`')
+ if (islower((unsigned char) *ptr) || *ptr == '$' || *ptr == '`')
hasletter = 1;
if (hasletter && *ptr == '\n') {
isbinary = 0;
@@ -570,9 +611,22 @@ zexecve(char *pth, char **argv, char **newenvp)
}
}
if (!isbinary) {
- argv[-1] = "sh";
+ char** args = argv;
+ if (argv[0][0] == '-' || argv[0][0] == '+') {
+ /*
+ * guard against +foo or -bar script paths being
+ * taken as options. POSIX says the script path
+ * must be passed as an *operand*. "--" would also
+ * make sure the next argument is treated as an
+ * operand with POSIX compliant sh implementations
+ * but "-" is more portable (to the Bourne shell in
+ * particular) and shorter.
+ */
+ *--args = "-";
+ }
+ *--args = "sh";
winch_unblock();
- execve("/bin/sh", argv - 1, newenvp);
+ execve("/bin/sh", args, newenvp);
}
}
} else
@@ -826,6 +880,10 @@ execute(LinkList args, int flags, int defpath)
_realexit();
else
zerr("command not found: %s", arg0);
+ /* This is bash behavior, but fails to restore interactive settings etc.
+ lastval = ((eno == EACCES || eno == ENOEXEC) ? 126 : 127);
+ return;
+ */
_exit((eno == EACCES || eno == ENOEXEC) ? 126 : 127);
}
@@ -1034,7 +1092,7 @@ entersubsh(int flags, struct entersubsh_ret *retp)
int i, sig, monitor, job_control_ok;
if (!(flags & ESUB_KEEPTRAP))
- for (sig = 0; sig < SIGCOUNT; sig++)
+ for (sig = 0; sig <= SIGCOUNT; sig++)
if (!(sigtrapped[sig] & ZSIG_FUNC) &&
!(isset(POSIXTRAPS) && (sigtrapped[sig] & ZSIG_IGNORED)))
unsettrap(sig);
@@ -1148,7 +1206,7 @@ entersubsh(int flags, struct entersubsh_ret *retp)
* Start loop at 1 because 0 is SIGEXIT
*/
if (intrap)
- for (sig = 1; sig < SIGCOUNT; sig++)
+ for (sig = 1; sig <= SIGCOUNT; sig++)
if (sigtrapped[sig] && sigtrapped[sig] != ZSIG_IGNORED)
signal_unblock(signal_mask(sig));
if (!job_control_ok)
@@ -1328,7 +1386,7 @@ execlist(Estate state, int dont_change_job, int exiting)
*list_pipe_text = '\0';
}
- /* Loop over all sets of comands separated by newline, *
+ /* Loop over all sets of commands separated by newline, *
* semi-colon or ampersand (`sublists'). */
code = *state->pc++;
if (wc_code(code) != WC_LIST) {
@@ -1373,7 +1431,7 @@ execlist(Estate state, int dont_change_job, int exiting)
int oerrexit_opt = opts[ERREXIT];
Param pm;
opts[ERREXIT] = 0;
- noerrexit = NOERREXIT_EXIT | NOERREXIT_RETURN;
+ noerrexit |= NOERREXIT_EXIT | NOERREXIT_RETURN;
if (ltype & Z_SIMPLE) /* skip the line number */
pc2++;
pm = assignsparam("ZSH_DEBUG_CMD",
@@ -1424,17 +1482,13 @@ execlist(Estate state, int dont_change_job, int exiting)
goto sublist_done;
}
while (wc_code(code) == WC_SUBLIST) {
- int isend = (WC_SUBLIST_TYPE(code) == WC_SUBLIST_END);
+ this_noerrexit = 0;
+ int isandor = WC_SUBLIST_TYPE(code) != WC_SUBLIST_END;
+ int isnot = WC_SUBLIST_FLAGS(code) & WC_SUBLIST_NOT;
next = state->pc + WC_SUBLIST_SKIP(code);
- if (!oldnoerrexit)
- noerrexit = isend ? 0 : NOERREXIT_EXIT | NOERREXIT_RETURN;
- if (WC_SUBLIST_FLAGS(code) & WC_SUBLIST_NOT) {
- /* suppress errexit for "! this_command" */
- if (isend)
- this_noerrexit = 1;
- /* suppress errexit for ! <list-of-shell-commands> */
- noerrexit = NOERREXIT_EXIT | NOERREXIT_RETURN;
- }
+ /* suppress errexit for commands before && and || and after ! */
+ if (isandor || isnot)
+ noerrexit |= NOERREXIT_EXIT | NOERREXIT_RETURN;
switch (WC_SUBLIST_TYPE(code)) {
case WC_SUBLIST_END:
/* End of sublist; just execute, ignoring status. */
@@ -1443,6 +1497,9 @@ execlist(Estate state, int dont_change_job, int exiting)
else
execpline(state, code, ltype, (ltype & Z_END) && exiting);
state->pc = next;
+ /* suppress errexit for the command "! ..." */
+ if (isnot)
+ this_noerrexit = 1;
goto sublist_done;
break;
case WC_SUBLIST_AND:
@@ -1450,7 +1507,7 @@ execlist(Estate state, int dont_change_job, int exiting)
* we find a sublist followed by ORNEXT. */
if ((ret = ((WC_SUBLIST_FLAGS(code) & WC_SUBLIST_SIMPLE) ?
execsimple(state) :
- execpline(state, code, Z_SYNC, 0)))) {
+ execpline(state, code, Z_SYNC, 0))) || breaks) {
state->pc = next;
code = *state->pc++;
next = state->pc + WC_SUBLIST_SKIP(code);
@@ -1483,7 +1540,7 @@ execlist(Estate state, int dont_change_job, int exiting)
* we find a sublist followed by ANDNEXT. */
if (!(ret = ((WC_SUBLIST_FLAGS(code) & WC_SUBLIST_SIMPLE) ?
execsimple(state) :
- execpline(state, code, Z_SYNC, 0)))) {
+ execpline(state, code, Z_SYNC, 0))) || breaks) {
state->pc = next;
code = *state->pc++;
next = state->pc + WC_SUBLIST_SKIP(code);
@@ -1514,18 +1571,12 @@ execlist(Estate state, int dont_change_job, int exiting)
}
state->pc = next;
code = *state->pc++;
+ noerrexit = oldnoerrexit;
}
state->pc--;
sublist_done:
- /*
- * See hairy code near the end of execif() for the
- * following. "noerrexit " only applies until
- * we hit execcmd on the way down. We're now
- * on the way back up, so don't restore it.
- */
- if (!(oldnoerrexit & NOERREXIT_UNTIL_EXEC))
- noerrexit = oldnoerrexit;
+ noerrexit = oldnoerrexit;
if (sigtrapped[SIGDEBUG] && !isset(DEBUGBEFORECMD) && !donedebug) {
/*
@@ -1534,7 +1585,7 @@ sublist_done:
*/
int oerrexit_opt = opts[ERREXIT];
opts[ERREXIT] = 0;
- noerrexit = NOERREXIT_EXIT | NOERREXIT_RETURN;
+ noerrexit |= NOERREXIT_EXIT | NOERREXIT_RETURN;
exiting = donetrap;
ret = lastval;
dotrap(SIGDEBUG);
@@ -1564,6 +1615,7 @@ sublist_done:
(isset(ERRRETURN) && !errreturn)) &&
!(noerrexit & NOERREXIT_EXIT);
if (errexit) {
+ errflag = 0;
if (sigtrapped[SIGEXIT])
dotrap(SIGEXIT);
if (mypid != getpid())
@@ -1581,6 +1633,7 @@ sublist_done:
break;
code = *state->pc++;
}
+ this_noerrexit = 0;
pline_level = old_pline_level;
list_pipe = old_list_pipe;
list_pipe_job = old_list_pipe_job;
@@ -1595,9 +1648,12 @@ sublist_done:
thisjob = cj;
if (exiting && sigtrapped[SIGEXIT]) {
+ int eflag = errflag;
+ errflag = 0; /* Clear the context for trap */
dotrap(SIGEXIT);
/* Make sure this doesn't get executed again. */
sigtrapped[SIGEXIT] = 0;
+ errflag = eflag;
}
unqueue_signals();
@@ -1624,7 +1680,7 @@ execpline(Estate state, wordcode slcode, int how, int last1)
wordcode code = *state->pc++;
static int lastwj, lpforked;
- if (wc_code(code) != WC_PIPE)
+ if (wc_code(code) != WC_PIPE && !(how & Z_TIMED))
return lastval = (slflags & WC_SUBLIST_NOT) != 0;
else if (slflags & WC_SUBLIST_NOT)
last1 = 0;
@@ -1806,7 +1862,7 @@ execpline(Estate state, wordcode slcode, int how, int last1)
(jobtab[list_pipe_job].stat & STAT_STOPPED)))) {
pid_t pid = 0;
int synch[2];
- struct timeval bgtime;
+ struct timespec bgtime;
/*
* A pipeline with the shell handling the right
@@ -1899,8 +1955,12 @@ execpline(Estate state, wordcode slcode, int how, int last1)
break;
}
}
- else if (subsh && jn->stat & STAT_STOPPED)
- thisjob = newjob;
+ else if (subsh && jn->stat & STAT_STOPPED) {
+ if (thisjob == newjob)
+ makerunning(jn);
+ else
+ thisjob = newjob;
+ }
else
break;
}
@@ -1922,7 +1982,7 @@ execpline(Estate state, wordcode slcode, int how, int last1)
}
else
unqueue_signals();
- if ((slflags & WC_SUBLIST_NOT) && !errflag)
+ if ((slflags & WC_SUBLIST_NOT) && !errflag && !retflag)
lastval = !lastval;
}
if (!pline_level)
@@ -2223,7 +2283,7 @@ closemn(struct multio **mfds, int fd, int type)
char buf[TCBUFSIZE];
int len, i;
pid_t pid;
- struct timeval bgtime;
+ struct timespec bgtime;
/*
* We need to block SIGCHLD in case the process
@@ -2247,6 +2307,8 @@ closemn(struct multio **mfds, int fd, int type)
return;
}
/* pid == 0 */
+ opts[INTERACTIVE] = 0;
+ dont_queue_signals();
child_unblock();
closeallelse(mn);
if (mn->rflag) {
@@ -2259,19 +2321,21 @@ closemn(struct multio **mfds, int fd, int type)
break;
}
for (i = 0; i < mn->ct; i++)
- write_loop(mn->fds[i], buf, len);
+ if (write_loop(mn->fds[i], buf, len) < 0)
+ break;
}
} else {
/* cat process */
for (i = 0; i < mn->ct; i++)
while ((len = read(mn->fds[i], buf, TCBUFSIZE)) != 0) {
if (len < 0) {
- if (errno == EINTR)
+ if (errno == EINTR && !isatty(mn->fds[i]))
continue;
else
break;
}
- write_loop(mn->pipe, buf, len);
+ if (write_loop(mn->pipe, buf, len) < 0)
+ break;
}
}
_exit(0);
@@ -2345,7 +2409,7 @@ addfd(int forked, int *save, struct multio **mfds, int fd1, int fd2, int rflag,
/* fd will be over 10, don't touch mfds */
fd1 = movefd(fd2);
if (fd1 == -1) {
- zerr("cannot moved fd %d: %e", fd2, errno);
+ zerr("cannot move fd %d: %e", fd2, errno);
return;
} else {
fdtable[fd1] = FDT_EXTERNAL;
@@ -2544,6 +2608,17 @@ addvars(Estate state, Wordcode pc, int addflags)
opts[ALLEXPORT] = allexp;
} else
pm = assignsparam(name, val, myflags);
+ if (!pm) {
+ lastval = 1;
+ /*
+ * This is cheating but some exec functions propagate
+ * assignment status only from command substitution
+ *
+ * zerr("%s: assignment failed", name);
+ */
+ if (!cmdoutval)
+ cmdoutval = 1;
+ }
if (errflag) {
state->pc = opc;
return;
@@ -2568,7 +2643,16 @@ addvars(Estate state, Wordcode pc, int addflags)
}
fprintf(xtrerr, ") ");
}
- assignaparam(name, arr, myflags);
+ if (!assignaparam(name, arr, myflags)) {
+ lastval = 1;
+ /*
+ * See above RE "cheating"
+ *
+ * zerr("%s: array assignment failed", name);
+ */
+ if (!cmdoutval)
+ cmdoutval = 1;
+ }
if (errflag) {
state->pc = opc;
return;
@@ -2744,7 +2828,7 @@ execcmd_fork(Estate state, int how, int type, Wordcode varspc,
pid_t pid;
int synch[2], flags;
struct entersubsh_ret esret;
- struct timeval bgtime;
+ struct timespec bgtime;
child_block();
esret.gleader = -1;
@@ -2858,6 +2942,14 @@ execcmd_exec(Estate state, Execcmd_params eparams,
*/
LinkList preargs;
+ /*
+ * for the "time" keyword
+ */
+ child_times_t shti, chti;
+ struct timespec then;
+ if (how & Z_TIMED)
+ shelltime(&shti, &chti, &then, 0);
+
doneps4 = 0;
/*
@@ -2941,7 +3033,7 @@ execcmd_exec(Estate state, Execcmd_params eparams,
/*
* preargs contains args that have been expanded by prefork.
* Running execcmd_getargs() causes any argument available
- * in args to be exanded where necessary and transferred to
+ * in args to be expanded where necessary and transferred to
* preargs. We call execcmd_getargs() every time we need to
* analyse an argument not available in preargs, though there is
* no guarantee a further argument will be available.
@@ -2990,6 +3082,8 @@ execcmd_exec(Estate state, Execcmd_params eparams,
if (!(hn = resolvebuiltin(cmdarg, hn))) {
if (forked)
_realexit();
+ if (how & Z_TIMED)
+ shelltime(&shti, &chti, &then, 1);
return;
}
if (type != WC_TYPESET)
@@ -3171,6 +3265,8 @@ execcmd_exec(Estate state, Execcmd_params eparams,
errflag |= ERRFLAG_ERROR;
if (forked)
_realexit();
+ if (how & Z_TIMED)
+ shelltime(&shti, &chti, &then, 1);
return;
}
}
@@ -3200,10 +3296,6 @@ execcmd_exec(Estate state, Execcmd_params eparams,
} else
preargs = NULL;
- /* if we get this far, it is OK to pay attention to lastval again */
- if (noerrexit & NOERREXIT_UNTIL_EXEC)
- noerrexit = 0;
-
/* Do prefork substitutions.
*
* Decide if we need "magic" handling of ~'s etc. in
@@ -3266,6 +3358,8 @@ execcmd_exec(Estate state, Execcmd_params eparams,
errflag |= ERRFLAG_ERROR;
if (forked)
_realexit();
+ if (how & Z_TIMED)
+ shelltime(&shti, &chti, &then, 1);
return;
} else if (!nullcmd || !*nullcmd || opts[SHNULLCMD]) {
if (!args)
@@ -3286,6 +3380,8 @@ execcmd_exec(Estate state, Execcmd_params eparams,
lastval = 0;
if (forked)
_realexit();
+ if (how & Z_TIMED)
+ shelltime(&shti, &chti, &then, 1);
return;
} else {
/*
@@ -3298,6 +3394,8 @@ execcmd_exec(Estate state, Execcmd_params eparams,
lastval = 1;
if (forked)
_realexit();
+ if (how & Z_TIMED)
+ shelltime(&shti, &chti, &then, 1);
return;
}
cmdoutval = use_cmdoutval ? lastval : 0;
@@ -3316,6 +3414,8 @@ execcmd_exec(Estate state, Execcmd_params eparams,
}
if (forked)
_realexit();
+ if (how & Z_TIMED)
+ shelltime(&shti, &chti, &then, 1);
return;
}
} else if (isset(RESTRICTED) && (cflags & BINF_EXEC) && do_exec) {
@@ -3324,6 +3424,8 @@ execcmd_exec(Estate state, Execcmd_params eparams,
lastval = 1;
if (forked)
_realexit();
+ if (how & Z_TIMED)
+ shelltime(&shti, &chti, &then, 1);
return;
}
@@ -3360,6 +3462,8 @@ execcmd_exec(Estate state, Execcmd_params eparams,
opts[AUTOCONTINUE] = oautocont;
if (forked)
_realexit();
+ if (how & Z_TIMED)
+ shelltime(&shti, &chti, &then, 1);
return;
}
break;
@@ -3371,6 +3475,8 @@ execcmd_exec(Estate state, Execcmd_params eparams,
if (!(hn = resolvebuiltin(cmdarg, hn))) {
if (forked)
_realexit();
+ if (how & Z_TIMED)
+ shelltime(&shti, &chti, &then, 1);
return;
}
break;
@@ -3389,6 +3495,8 @@ execcmd_exec(Estate state, Execcmd_params eparams,
opts[AUTOCONTINUE] = oautocont;
if (forked)
_realexit();
+ if (how & Z_TIMED)
+ shelltime(&shti, &chti, &then, 1);
return;
}
@@ -3468,6 +3576,8 @@ execcmd_exec(Estate state, Execcmd_params eparams,
opts[AUTOCONTINUE] = oautocont;
if (forked)
_realexit();
+ if (how & Z_TIMED)
+ shelltime(&shti, &chti, &then, 1);
return;
}
}
@@ -3498,6 +3608,8 @@ execcmd_exec(Estate state, Execcmd_params eparams,
opts[AUTOCONTINUE] = oautocont;
if (forked)
_realexit();
+ if (how & Z_TIMED)
+ shelltime(&shti, &chti, &then, 1);
return;
}
@@ -3712,7 +3824,7 @@ execcmd_exec(Estate state, Execcmd_params eparams,
addfd(forked, save, mfds, fn->fd1, fil, 0, fn->varid);
/* If this is 'exec < file', read from stdin, *
* not terminal, unless `file' is a terminal. */
- if (nullexec == 1 && fn->fd1 == 0 &&
+ if (nullexec == 1 && fn->fd1 == 0 && !fn->varid &&
isset(SHINSTDIN) && interact && !zleactive)
init_io(NULL);
break;
@@ -4300,11 +4412,16 @@ execcmd_exec(Estate state, Execcmd_params eparams,
errflag |= ERRFLAG_ERROR;
}
}
+ if ((is_cursh || do_exec) && (how & Z_TIMED))
+ shelltime(&shti, &chti, &then, 1);
if (newxtrerr) {
+ int eno = errno;
fil = fileno(newxtrerr);
fclose(newxtrerr);
xtrerr = oxtrerr;
+ /* Call zclose() to clean up internal tables, ignore EBADF */
zclose(fil);
+ errno = eno;
}
zsfree(STTYval);
@@ -4329,7 +4446,7 @@ save_params(Estate state, Wordcode pc, LinkList *restore_p, LinkList *remove_p)
while (wc_code(ac = *pc) == WC_ASSIGN) {
s = ecrawstr(state->prog, pc + 1, NULL);
if ((pm = (Param) paramtab->getnode(paramtab, s))) {
- Param tpm;
+ Param tpm = NULL;
if (pm->env)
delenv(pm);
if (!(pm->node.flags & PM_SPECIAL)) {
@@ -4346,7 +4463,6 @@ save_params(Estate state, Wordcode pc, LinkList *restore_p, LinkList *remove_p)
tpm = (Param) zshcalloc(sizeof *tpm);
tpm->node.nam = ztrdup(pm->node.nam);
copyparam(tpm, pm, 0);
- pm = tpm;
} else if (!(pm->node.flags & PM_READONLY) &&
(unset(RESTRICTED) || !(pm->node.flags & PM_RESTRICTED))) {
/*
@@ -4357,10 +4473,10 @@ save_params(Estate state, Wordcode pc, LinkList *restore_p, LinkList *remove_p)
tpm = (Param) hcalloc(sizeof *tpm);
tpm->node.nam = pm->node.nam;
copyparam(tpm, pm, 1);
- pm = tpm;
}
addlinknode(*remove_p, dupstring(s));
- addlinknode(*restore_p, pm);
+ if (tpm)
+ addlinknode(*restore_p, tpm);
} else
addlinknode(*remove_p, dupstring(s));
@@ -4636,6 +4752,9 @@ getoutput(char *cmd, int qt)
if (!prog)
return NULL;
+ if (!isset(EXECOPT))
+ return newlinklist();
+
if ((s = simple_redir_name(prog, REDIR_READ))) {
/* $(< word) */
int stream;
@@ -4852,7 +4971,6 @@ getoutputfile(char *cmd, char **eptr)
if ((fd = open(nam, O_WRONLY | O_CREAT | O_EXCL | O_NOCTTY, 0600)) < 0) {
zerr("process substitution failed: %e", errno);
- free(nam);
if (!s)
child_unblock();
return NULL;
@@ -4941,7 +5059,7 @@ getproc(char *cmd, char **eptr)
int out = *cmd == Inang;
char *pnam;
pid_t pid;
- struct timeval bgtime;
+ struct timespec bgtime;
#ifndef PATH_DEV_FD
int fd;
@@ -5030,7 +5148,7 @@ getpipe(char *cmd, int nullexec)
Eprog prog;
int pipes[2], out = *cmd == Inang;
pid_t pid;
- struct timeval bgtime;
+ struct timespec bgtime;
char *ends;
if (!(prog = parsecmd(cmd, &ends)))
@@ -5052,7 +5170,7 @@ getpipe(char *cmd, int nullexec)
procsubstpid = pid;
return pipes[!out];
}
- entersubsh(ESUB_ASYNC|ESUB_PGRP, NULL);
+ entersubsh(ESUB_ASYNC|ESUB_PGRP|ESUB_NOMONITOR, NULL);
redup(pipes[out], out);
closem(FDT_UNUSED, 0); /* this closes pipes[!out] as well */
cmdpush(CS_CMDSUBST);
@@ -5184,7 +5302,7 @@ exectime(Estate state, UNUSED(int do_exec))
jb = thisjob;
if (WC_TIMED_TYPE(state->pc[-1]) == WC_TIMED_EMPTY) {
- shelltime();
+ shelltime(NULL,NULL,NULL,0);
return 0;
}
execpline(state, *state->pc++, Z_TIMED|Z_SYNC, 0);
@@ -5366,7 +5484,7 @@ execfuncdef(Estate state, Eprog redir_prog)
} else {
/* is this shell function a signal trap? */
if (!strncmp(s, "TRAP", 4) &&
- (signum = getsignum(s + 4)) != -1) {
+ (signum = getsigidx(s + 4)) != -1) {
if (settrap(signum, NULL, ZSIG_FUNC)) {
freeeprog(shf->funcdef);
dircache_set(&shf->filename, NULL);
@@ -5385,7 +5503,8 @@ execfuncdef(Estate state, Eprog redir_prog)
if (funcstack && funcstack->tp == FS_FUNC &&
!strcmp(s, funcstack->name)) {
Shfunc old = ((Shfunc)shfunctab->getnode(shfunctab, s));
- shf->node.flags |= old->node.flags & (PM_TAGGED|PM_TAGGED_LOCAL);
+ if (old)
+ shf->node.flags |= old->node.flags & (PM_TAGGED|PM_TAGGED_LOCAL);
}
shfunctab->addnode(shfunctab, ztrdup(s), shf);
}
@@ -5734,12 +5853,25 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
char *name = shfunc->node.nam;
int flags = shfunc->node.flags;
char *fname = dupstring(name);
- Eprog prog;
+ Eprog prog, marked_prog;
static int oflags;
static int funcdepth;
Heap funcheap;
queue_signals(); /* Lots of memory and global state changes coming */
+ /*
+ * In case this is a special function such as a trap, mark it
+ * as in use right now, so it doesn't get freed early. The
+ * worst that can happen is this hangs around in memory a little
+ * longer than strictly needed.
+ *
+ * Classic example of this happening is running TRAPEXIT directly.
+ *
+ * Because the shell function's contents may change, we'll ensure
+ * we use a consistent structure for use / free.
+ */
+ marked_prog = shfunc->funcdef;
+ useeprog(marked_prog);
NEWHEAPS(funcheap) {
/*
@@ -5773,6 +5905,22 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
memcpy(funcsave->pipestats, pipestats, bytes);
}
+ if (!strcmp(fname, "TRAPEXIT")) {
+ /*
+ * If we are executing TRAPEXIT directly, starttrapscope()
+ * will pull the rug out from under us to ensure the
+ * exit trap isn't run inside the function. We just need
+ * the information locally here, so copy it on the heap.
+ *
+ * The funcdef is separately handled by reference counting.
+ */
+ Shfunc shcopy = (Shfunc)zhalloc(sizeof(struct shfunc));
+ memcpy(shcopy, shfunc, sizeof(struct shfunc));
+ shcopy->node.nam = dupstring(shfunc->node.nam);
+ shfunc = shcopy;
+ name = shfunc->node.nam;
+ }
+
starttrapscope();
startpatternscope();
@@ -5897,6 +6045,8 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
funcsave->fstack.filename = getshfuncfile(shfunc);
prog = shfunc->funcdef;
+ DPUTS1(!prog->nref, "function definition %s has zero reference count",
+ (fname && *fname) ? fname : "<anon>");
if (prog->flags & EF_RUN) {
Shfunc shf;
@@ -5925,15 +6075,6 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
* This function is forced to return.
*/
retflag = 0;
- /*
- * The calling function isn't necessarily forced to return,
- * but it should be made sensitive to ERR_EXIT and
- * ERR_RETURN as the assumptions we made at the end of
- * constructs within this function no longer apply. If
- * there are cases where this is not true, they need adding
- * to C03traps.ztst.
- */
- this_noerrexit = 0;
breaks = funcsave->breaks;
}
freearray(pparams);
@@ -5961,11 +6102,23 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
emulation = funcsave->emulation;
sticky = funcsave->sticky;
} else if (isset(LOCALOPTIONS)) {
+ /* we need to call inittyptab() if these options change */
+ int init_typtab =
+#ifdef MULTIBYTE_SUPPORT
+ funcsave->opts[MULTIBYTE] != opts[MULTIBYTE] ||
+#endif
+ funcsave->opts[BANGHIST] != opts[BANGHIST] ||
+ funcsave->opts[SHINSTDIN] != opts[SHINSTDIN];
+ /* take care of SUNKEYBOARDHACK but not of EMACS/VI */
+ if (funcsave->opts[SUNKEYBOARDHACK] != opts[SUNKEYBOARDHACK])
+ keyboardhackchar = funcsave->opts[SUNKEYBOARDHACK] ? '`' : '\0';
/* restore all shell options except PRIVILEGED and RESTRICTED */
funcsave->opts[PRIVILEGED] = opts[PRIVILEGED];
funcsave->opts[RESTRICTED] = opts[RESTRICTED];
memcpy(opts, funcsave->opts, sizeof(opts));
emulation = funcsave->emulation;
+ if (init_typtab)
+ inittyptab();
} else {
/* just restore a couple. */
opts[XTRACE] = funcsave->opts[XTRACE];
@@ -5998,6 +6151,7 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
}
} OLDHEAPS;
+ freeeprog(marked_prog);
unqueue_signals();
/*
diff --git a/Src/glob.c b/Src/glob.c
index 400be12d5..3e34f708e 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -133,7 +133,7 @@ typedef struct stat *Statptr; /* This makes the Ultrix compiler happy. Go figu
#define TT_TERABYTES 5
-typedef int (*TestMatchFunc) _((char *, struct stat *, off_t, char *));
+typedef int (*TestMatchFunc) (char *, struct stat *, off_t, char *);
struct qual {
struct qual *next; /* Next qualifier, must match */
@@ -1264,7 +1264,7 @@ zglob(LinkList list, LinkNode np, int nountok)
int sense, qualsfound;
off_t data;
char *sdata, *newcolonmod, *ptr;
- int (*func) _((char *, Statptr, off_t, char *));
+ int (*func) (char *, Statptr, off_t, char *);
/*
* Initialise state variables for current file pattern.
@@ -1310,14 +1310,15 @@ zglob(LinkList list, LinkNode np, int nountok)
if (*ptr == Dash)
*ptr = '-';
while (*s && !newcolonmod) {
- func = (int (*) _((char *, Statptr, off_t, char *)))0;
+ func = (int (*) (char *, Statptr, off_t, char *)) 0;
if (*s == ',') {
/* A comma separates alternative sets of qualifiers */
s++;
sense = 0;
if (qualct) {
qn = (struct qual *)hcalloc(sizeof *qn);
- qo->or = qn;
+ if (qo)
+ qo->or = qn;
qo = qn;
qualorct++;
qualct = 0;
@@ -1481,7 +1482,7 @@ zglob(LinkList list, LinkNode np, int nountok)
sav = *tt;
*tt = '\0';
- if ((pw = getpwnam(s + arglen)))
+ if ((pw = getpwnam(unmeta(s + arglen))))
data = pw->pw_uid;
else {
zerr("unknown username '%s'", s + arglen);
@@ -1960,7 +1961,7 @@ zglob(LinkList list, LinkNode np, int nountok)
/* Sort arguments in to lexical (and possibly numeric) order. *
* This is reversed to facilitate insertion into the list. */
qsort((void *) & matchbuf[0], matchct, sizeof(struct gmatch),
- (int (*) _((const void *, const void *)))gmatchcmp);
+ (int (*) (const void *, const void *)) gmatchcmp);
}
if (first < 0) {
@@ -2418,11 +2419,11 @@ xpandbraces(LinkList list, LinkNode *np)
memset(ccl, 0, sizeof(ccl) / sizeof(ccl[0]));
for (p = str + 1; p < str2;) {
if (itok(c1 = *p++))
- c1 = ztokens[c1 - STOUC(Pound)];
+ c1 = ztokens[c1 - (unsigned char) Pound];
if ((char) c1 == Meta)
c1 = 32 ^ *p++;
if (itok(c2 = *p))
- c2 = ztokens[c2 - STOUC(Pound)];
+ c2 = ztokens[c2 - (unsigned char) Pound];
if ((char) c2 == Meta)
c2 = 32 ^ p[1];
if (IS_DASH((char)c1) && lastch >= 0 &&
diff --git a/Src/hashtable.c b/Src/hashtable.c
index bb165505e..96675a393 100644
--- a/Src/hashtable.c
+++ b/Src/hashtable.c
@@ -836,10 +836,10 @@ static HashNode
removeshfuncnode(UNUSED(HashTable ht), const char *nam)
{
HashNode hn;
- int signum;
+ int sigidx;
- if (!strncmp(nam, "TRAP", 4) && (signum = getsignum(nam + 4)) != -1)
- hn = removetrap(signum);
+ if (!strncmp(nam, "TRAP", 4) && (sigidx = getsigidx(nam + 4)) != -1)
+ hn = removetrap(sigidx);
else
hn = removehashnode(shfunctab, nam);
@@ -856,10 +856,10 @@ disableshfuncnode(HashNode hn, UNUSED(int flags))
{
hn->flags |= DISABLED;
if (!strncmp(hn->nam, "TRAP", 4)) {
- int signum = getsignum(hn->nam + 4);
- if (signum != -1) {
- sigtrapped[signum] &= ~ZSIG_FUNC;
- unsettrap(signum);
+ int sigidx = getsigidx(hn->nam + 4);
+ if (sigidx != -1) {
+ sigtrapped[sigidx] &= ~ZSIG_FUNC;
+ unsettrap(sigidx);
}
}
}
@@ -876,9 +876,9 @@ enableshfuncnode(HashNode hn, UNUSED(int flags))
shf->node.flags &= ~DISABLED;
if (!strncmp(shf->node.nam, "TRAP", 4)) {
- int signum = getsignum(shf->node.nam + 4);
- if (signum != -1) {
- settrap(signum, NULL, ZSIG_FUNC);
+ int sigidx = getsigidx(shf->node.nam + 4);
+ if (sigidx != -1) {
+ settrap(sigidx, NULL, ZSIG_FUNC);
}
}
}
@@ -1397,6 +1397,14 @@ histstrcmp(const char *str1, const char *str2)
{
while (inblank(*str1)) str1++;
while (inblank(*str2)) str2++;
+
+ /* If insignificant whitespace has already been eliminated,
+ * there is no reason to expend similar effort here. Also,
+ * this is more accurate in cases of quoted whitespace.
+ */
+ if (isset(HISTREDUCEBLANKS))
+ return strcmp(str1, str2);
+
while (*str1 && *str2) {
if (inblank(*str1)) {
if (!inblank(*str2))
diff --git a/Src/hist.c b/Src/hist.c
index bff0abe61..00bdbb2b8 100644
--- a/Src/hist.c
+++ b/Src/hist.c
@@ -34,25 +34,25 @@
* word control. */
/**/
-mod_export int (*hgetc) _((void));
+mod_export int (*hgetc) (void);
/**/
-void (*hungetc) _((int));
+void (*hungetc) (int);
/**/
-void (*hwaddc) _((int));
+void (*hwaddc) (int);
/**/
-void (*hwbegin) _((int));
+void (*hwbegin) (int);
/**/
-void (*hwabort) _((void));
+void (*hwabort) (void);
/**/
-void (*hwend) _((void));
+void (*hwend) (void);
/**/
-void (*addtoline) _((int));
+void (*addtoline) (int);
/* != 0 means history substitution is turned off */
@@ -163,6 +163,11 @@ char *hsubl;
/**/
char *hsubr;
+/* state of histsubstpattern at last substitution */
+
+/**/
+int hsubpatopt;
+
/* pointer into the history line */
/**/
@@ -624,7 +629,7 @@ histsubchar(int c)
return substfailed();
if (!hsubl)
return -1;
- if (subst(&sline, hsubl, hsubr, gbal))
+ if (subst(&sline, hsubl, hsubr, gbal, 0))
return substfailed();
} else {
/* Line doesn't begin ^foo^bar */
@@ -831,7 +836,7 @@ histsubchar(int c)
if ((c = ingetc()) == 'g') {
gbal = 1;
c = ingetc();
- if (c != 's' && c != '&') {
+ if (c != 's' && c != 'S' && c != '&') {
zerr("'s' or '&' modifier expected after 'g'");
return -1;
}
@@ -891,11 +896,13 @@ histsubchar(int c)
}
break;
case 's':
+ case 'S':
+ hsubpatopt = (c == 'S');
if (getsubsargs(sline, &gbal, &cflag))
return -1; /* fall through */
case '&':
if (hsubl && hsubr) {
- if (subst(&sline, hsubl, hsubr, gbal))
+ if (subst(&sline, hsubl, hsubr, gbal, hsubpatopt))
return substfailed();
} else {
herrflush();
@@ -1352,7 +1359,8 @@ putoldhistentryontop(short keep_going)
do {
if (max_unique_ct-- <= 0 || he == hist_ring) {
max_unique_ct = 0;
- he = hist_ring->down;
+ if (hist_ring)
+ he = hist_ring->down;
next = hist_ring;
break;
}
@@ -1360,12 +1368,16 @@ putoldhistentryontop(short keep_going)
next = he->down;
} while (!(he->node.flags & HIST_DUP));
}
- if (he != hist_ring->down) {
+ /* Is it really possible for hist_ring to be NULL here? */
+ if (he && (!hist_ring || he != hist_ring->down)) {
he->up->down = he->down;
he->down->up = he->up;
he->up = hist_ring;
- he->down = hist_ring->down;
- hist_ring->down = he->down->up = he;
+ if (hist_ring) {
+ he->down = hist_ring->down;
+ hist_ring->down = he;
+ }
+ he->down->up = he;
}
hist_ring = he;
}
@@ -1461,7 +1473,7 @@ should_ignore_line(Eprog prog)
mod_export int
hend(Eprog prog)
{
- int flag, hookret, stack_pos = histsave_stack_pos;
+ int flag, hookret = 0, stack_pos = histsave_stack_pos;
/*
* save:
* 0: don't save
@@ -1643,12 +1655,17 @@ hend(Eprog prog)
void
ihwbegin(int offset)
{
+ int pos = hptr - chline + offset;
if (stophist == 2 || (histactive & HA_INWORD) ||
(inbufflags & (INP_ALIAS|INP_HIST)) == INP_ALIAS)
return;
if (chwordpos%2)
chwordpos--; /* make sure we're on a word start, not end */
- chwords[chwordpos++] = hptr - chline + offset;
+ DPUTS1(pos < 0, "History word position < 0 in %s",
+ dupstrpfx(chline, hptr-chline));
+ if (pos < 0)
+ pos = 0;
+ chwords[chwordpos++] = pos;
}
/* Abort current history word, not needed */
@@ -2235,7 +2252,7 @@ casemodify(char *str, int how)
char *mbptr;
for (mbptr = mbstr; mbptr < mbstr + len2; mbptr++) {
- if (imeta(STOUC(*mbptr))) {
+ if (imeta((unsigned char) *mbptr)) {
*ptr2++ = Meta;
*ptr2++ = *mbptr ^ 32;
} else
@@ -2254,10 +2271,10 @@ casemodify(char *str, int how)
int c;
int mod = 0;
if (*str == Meta) {
- c = STOUC(str[1] ^ 32);
+ c = (unsigned char) (str[1] ^ 32);
str += 2;
} else
- c = STOUC(*str++);
+ c = (unsigned char) *str++;
switch (how) {
case CASMOD_LOWER:
if (isupper(c)) {
@@ -2310,7 +2327,7 @@ casemodify(char *str, int how)
/**/
int
-subst(char **strptr, char *in, char *out, int gbal)
+subst(char **strptr, char *in, char *out, int gbal, int forcepat)
{
char *str = *strptr, *substcut, *sptr;
int off, inlen, outlen;
@@ -2318,7 +2335,7 @@ subst(char **strptr, char *in, char *out, int gbal)
if (!*in)
in = str, gbal = 0;
- if (isset(HISTSUBSTPATTERN)) {
+ if (isset(HISTSUBSTPATTERN) || forcepat) {
int fl = SUB_LONG|SUB_REST|SUB_RETFAIL;
char *oldin = in;
if (gbal)
@@ -2821,11 +2838,12 @@ readhistfile(char *fn, int err, int readflags)
*/
if (uselex || remeta)
freeheap();
- if (errflag & ERRFLAG_INT) {
- /* Can't assume fast read next time if interrupted. */
- lasthist.interrupted = 1;
+ if (errflag & ERRFLAG_INT)
break;
- }
+ }
+ if (errflag & ERRFLAG_INT) {
+ /* Can't assume fast read next time if interrupted. */
+ lasthist.interrupted = 1;
}
if (start && readflags & HFILE_USE_OPTIONS) {
zsfree(lasthist.text);
@@ -2874,9 +2892,9 @@ flockhistfile(char *fn, int keep_trying)
/*
* Timeout is ten seconds.
*/
- end_time = time(NULL) + (time_t)10;
+ end_time = zmonotime(NULL) + (time_t)10;
while (fcntl(flock_fd, F_SETLKW, &lck) == -1) {
- if (!keep_trying || time(NULL) >= end_time ||
+ if (!keep_trying || zmonotime(NULL) >= end_time ||
/*
* Randomise wait to minimise clashes with shells exiting at
* the same time.
@@ -3091,7 +3109,9 @@ savehistfile(char *fn, int err, int writeflags)
hist_ignore_all_dups |= isset(HISTSAVENODUPS);
readhistfile(fn, err, 0);
hist_ignore_all_dups = isset(HISTIGNOREALLDUPS);
- if (histlinect)
+ if (errflag & ERRFLAG_INT)
+ ret = -1;
+ else if (histlinect)
savehistfile(fn, err, 0);
pophiststack();
@@ -3120,7 +3140,7 @@ static int lockhistct;
static int
checklocktime(char *lockfile, long *sleep_usp, time_t then)
{
- time_t now = time(NULL);
+ time_t now = zmonotime(NULL);
if (now + 10 < then) {
/* File is more than 10 seconds in the future? */
@@ -3536,9 +3556,8 @@ bufferwords(LinkList list, char *buf, int *index, int flags)
} else if (buf) {
if (IS_REDIROP(tok) && tokfd >= 0) {
char b[20];
-
- sprintf(b, "%d%s", tokfd, tokstrings[tok]);
- addlinknode(list, dupstring(b));
+ int l = sprintf(b, "%d%s", tokfd, tokstrings[tok]);
+ addlinknode(list, dupstring_wlen(b, l));
num++;
} else if (tok != NEWLIN) {
addlinknode(list, dupstring(tokstrings[tok]));
@@ -3791,8 +3810,14 @@ histsplitwords(char *lineptr, short **wordsp, int *nwordsp, int *nwordposp,
zrealloc(words, nwords*sizeof(*words));
}
words[nwordpos++] = lineptr - start;
- while (*lineptr && !inblank(*lineptr))
- lineptr++;
+ while (*lineptr) {
+ if (*lineptr == Meta && lineptr[1])
+ lineptr += 2;
+ else if (!inblank(*lineptr))
+ lineptr++;
+ else
+ break;
+ }
words[nwordpos++] = lineptr - start;
}
} while (*lineptr);
diff --git a/Src/init.c b/Src/init.c
index 871d46b12..76de0b449 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -36,6 +36,10 @@
#include "version.h"
+#if defined(HAVE_SYS_SYSCTL_H) && !defined(__linux)
+#include <sys/sysctl.h>
+#endif
+
/**/
int noexitct = 0;
@@ -246,10 +250,12 @@ loop(int toplevel, int justonce)
static int restricted;
+/* original argv[0]. This is already metafied */
+static char *argv0;
+
/**/
static void
-parseargs(char *zsh_name, char **argv, char **runscript, char **cmdptr,
- int *needkeymap)
+parseargs(char *zsh_name, char **argv, char **runscript, char **cmdptr)
{
char **x;
LinkList paramlist;
@@ -257,7 +263,7 @@ parseargs(char *zsh_name, char **argv, char **runscript, char **cmdptr,
if (**argv == '-')
flags |= PARSEARGS_LOGIN;
- argzero = posixzero = *argv++;
+ argv0 = argzero = posixzero = *argv++;
SHIN = 0;
/*
@@ -266,7 +272,7 @@ parseargs(char *zsh_name, char **argv, char **runscript, char **cmdptr,
* matched by code at the end of the present function.
*/
- if (parseopts(zsh_name, &argv, opts, cmdptr, NULL, flags, needkeymap))
+ if (parseopts(zsh_name, &argv, opts, cmdptr, NULL, flags))
exit(1);
/*
@@ -377,7 +383,7 @@ static void parseopts_setemulate(char *nam, int flags)
/**/
mod_export int
parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp,
- LinkList optlist, int flags, int *needkeymap)
+ LinkList optlist, int flags)
{
int optionbreak = 0;
int action, optno;
@@ -483,14 +489,8 @@ parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp,
return 1;
} else if (optno == RESTRICTED && toplevel) {
restricted = action;
- } else if ((optno == EMACSMODE || optno == VIMODE)
- && (!toplevel || needkeymap)){
- if (!toplevel) {
- WARN_OPTION("can't change option: %s", *argv);
- } else {
- /* Need to wait for modules to be loadable */
- *needkeymap = optno;
- }
+ } else if ((optno == EMACSMODE || optno == VIMODE) && !toplevel) {
+ WARN_OPTION("can't change option: %s", *argv);
} else {
if (dosetopt(optno, action, toplevel, new_opts) &&
!toplevel) {
@@ -500,10 +500,10 @@ parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp,
}
}
break;
- } else if (isspace(STOUC(**argv))) {
+ } else if (isspace((unsigned char) **argv)) {
/* zsh's typtab not yet set, have to use ctype */
while (*++*argv)
- if (!isspace(STOUC(**argv))) {
+ if (!isspace((unsigned char) **argv)) {
badoptionstring:
WARN_OPTION("bad option string: '%s'", args);
return 1;
@@ -692,19 +692,18 @@ init_io(char *cmd)
} else
opts[USEZLE] = 0;
-#ifdef JOB_CONTROL
/* If interactive, make sure the shell is in the foreground and is the
* process group leader.
*/
mypid = (zlong)getpid();
- if (opts[MONITOR] && (SHTTY != -1)) {
- origpgrp = GETPGRP();
- acquire_pgrp(); /* might also clear opts[MONITOR] */
- } else
- opts[MONITOR] = 0;
-#else
- opts[MONITOR] = 0;
-#endif
+ if (opts[MONITOR]) {
+ if (SHTTY == -1)
+ opts[MONITOR] = 0;
+ else if (!origpgrp) {
+ origpgrp = GETPGRP();
+ acquire_pgrp(); /* might also clear opts[MONITOR] */
+ }
+ }
}
/**/
@@ -712,7 +711,7 @@ mod_export void
init_shout(void)
{
static char shoutbuf[BUFSIZ];
-#if defined(JOB_CONTROL) && defined(TIOCSETD) && defined(NTTYDISC)
+#if defined(TIOCSETD) && defined(NTTYDISC)
int ldisc;
#endif
@@ -723,7 +722,7 @@ init_shout(void)
return;
}
-#if defined(JOB_CONTROL) && defined(TIOCSETD) && defined(NTTYDISC)
+#if defined(TIOCSETD) && defined(NTTYDISC)
ldisc = NTTYDISC;
ioctl(SHTTY, TIOCSETD, (char *)&ldisc);
#endif
@@ -747,7 +746,7 @@ init_shout(void)
static char *tccapnams[TC_COUNT] = {
"cl", "le", "LE", "nd", "RI", "up", "UP", "do",
"DO", "dc", "DC", "ic", "IC", "cd", "ce", "al", "dl", "ta",
- "md", "so", "us", "me", "se", "ue", "ch",
+ "md", "mh", "so", "us", "ZH", "me", "se", "ue", "ZR", "ch",
"ku", "kd", "kl", "kr", "sc", "rc", "bc", "AF", "AB"
};
@@ -872,10 +871,141 @@ init_term(void)
/* The following is an attempt at a heuristic,
* but it fails in some cases */
/* rprompt_indent = ((hasam && !hasbw) || hasye || !tccan(TCLEFT)); */
+
+ /* if there's no termcap entry for italics, use CSI 3 m */
+ if (!tccan(TCITALICSBEG)) {
+ zsfree(tcstr[TCITALICSBEG]);
+ tcstr[TCITALICSBEG] = ztrdup("\033[3m");
+ tclen[TCITALICSBEG] = 4;
+ }
+ if (!tccan(TCITALICSEND)) {
+ zsfree(tcstr[TCITALICSEND]);
+ tcstr[TCITALICSEND] = ztrdup("\033[23m");
+ tclen[TCITALICSEND] = 5;
+ }
+ if (!tccan(TCFAINTBEG)) {
+ zsfree(tcstr[TCFAINTBEG]);
+ tcstr[TCFAINTBEG] = ztrdup("\033[2m");
+ tclen[TCFAINTBEG] = 4;
+ }
}
return 1;
}
+/*
+ * Get (or guess) the absolute pathname of the current zsh exeutable.
+ * Try OS-specific method, and if it fails, guess the absolute pathname
+ * from argv0, pwd, and PATH. 'name' and 'cwd' are unmetefied versions of
+ * argv0 and pwd.
+ * Returns a zalloc()ed string (not metafied), or NULL if failed.
+ */
+#ifdef __APPLE__
+#include <mach-o/dyld.h>
+#endif
+
+/**/
+static char *
+getmypath(const char *name, const char *cwd)
+{
+ char *buf;
+ int namelen;
+
+#if defined(__APPLE__)
+ {
+ uint32_t n = PATH_MAX;
+ int ret;
+ buf = (char *)zalloc(PATH_MAX);
+ if ((ret = _NSGetExecutablePath(buf, &n)) < 0) {
+ /* try again with increased buffer size */
+ buf = (char *)zrealloc(buf, n);
+ ret = _NSGetExecutablePath(buf, &n);
+ }
+ if (ret == 0 && strlen(buf) > 0)
+ return buf;
+ else
+ free(buf);
+ }
+#elif defined(KERN_PROC_PATHNAME)
+ {
+#ifdef __NetBSD__
+ int mib[4] = { CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_PATHNAME };
+#else
+ int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
+#endif
+ size_t len = PATH_MAX;
+ buf = (char *) zalloc(PATH_MAX);
+ if (!sysctl(mib, 4, buf, &len, NULL, 0) && len > 1)
+ return buf;
+ free(buf);
+ }
+#elif defined(PROC_SELF_EXE)
+ {
+ ssize_t n;
+ buf = (char *)zalloc(PATH_MAX);
+ n = readlink(PROC_SELF_EXE, buf, PATH_MAX);
+ if (n > 0 && n < PATH_MAX) {
+ buf[n] = '\0';
+ return buf;
+ }
+ else
+ free(buf);
+ }
+#endif
+
+ if (!name)
+ return NULL;
+ if (*name == '-')
+ ++name;
+ if ((namelen = strlen(name)) == 0)
+ return NULL;
+ /* guess the absolute pathname of 'name' */
+ if (name[namelen-1] == '/') /* name should not end with '/' */
+ return NULL;
+ else if (name[0] == '/') {
+ /* name is already an absolute pathname */
+ return ztrdup(name);
+ }
+ else if (strchr(name, '/')) {
+ /* relative path */
+ if (!cwd)
+ return NULL;
+ buf = (char *)zalloc(strlen(cwd) + namelen + 2);
+ sprintf(buf, "%s/%s", cwd, name);
+ return buf;
+ }
+#ifdef HAVE_REALPATH
+ else {
+ /* search each dir in PATH */
+ const char *path, *sep;
+ char *real, *try;
+ int pathlen, dirlen;
+
+ path = getenv("PATH");
+ if (!path || (pathlen = strlen(path)) == 0)
+ return NULL;
+ /* for simplicity, allocate buf even if REALPATH_ACCEPTS_NULL is on */
+ buf = (char *)zalloc(PATH_MAX);
+ try = (char *)zalloc(pathlen + namelen + 2);
+ do {
+ sep = strchr(path, ':');
+ dirlen = sep ? sep - path : strlen(path);
+ memcpy(try, path, dirlen);
+ try[dirlen] = '/';
+ try[dirlen+1] = '\0';
+ strcat(try, name);
+ real = realpath(try, buf);
+ if (sep)
+ path = sep + 1;
+ } while (!real && sep);
+ free(try);
+ if (!real)
+ free(buf);
+ return real; /* this may be NULL */
+ }
+#endif
+ return NULL;
+}
+
/* Initialize lots of global variables and hash tables */
/**/
@@ -885,7 +1015,6 @@ setupvals(char *cmd, char *runscript, char *zsh_name)
#ifdef USE_GETPWUID
struct passwd *pswd;
#endif
- struct timezone dummy_tz;
char *ptr;
int i, j;
#if defined(SITEFPATH_DIR) || defined(FPATH_DIR) || defined (ADDITIONAL_FPATH) || defined(FIXED_FPATH_DIR)
@@ -972,8 +1101,8 @@ setupvals(char *cmd, char *runscript, char *zsh_name)
hatchar = '^';
termflags = TERM_UNKNOWN;
curjob = prevjob = coprocin = coprocout = -1;
- gettimeofday(&shtimer, &dummy_tz); /* init $SECONDS */
- srand((unsigned int)(shtimer.tv_sec + shtimer.tv_usec)); /* seed $RANDOM */
+ zgettime_monotonic_if_available(&shtimer); /* init $SECONDS */
+ srand((unsigned int)(shtimer.tv_sec + shtimer.tv_nsec)); /* seed $RANDOM */
/* Set default path */
path = (char **) zalloc(sizeof(*path) * 5);
@@ -1067,9 +1196,12 @@ setupvals(char *cmd, char *runscript, char *zsh_name)
ztrdup(DEFAULT_IFS_SH) : ztrdup(DEFAULT_IFS);
wordchars = ztrdup(DEFAULT_WORDCHARS);
postedit = ztrdup("");
- zunderscore = (char *) zalloc(underscorelen = 32);
- underscoreused = 1;
- *zunderscore = '\0';
+ /* If _ is set in environment then initialize our $_ by copying it */
+ zunderscore = getenv("_");
+ zunderscore = zunderscore ? metafy(zunderscore, -1, META_DUP) : ztrdup("");
+ underscoreused = strlen(zunderscore) + 1;
+ underscorelen = (underscoreused + 31) & ~31;
+ zunderscore = (char *)zrealloc(zunderscore, underscorelen);
zoptarg = ztrdup("");
zoptind = 1;
@@ -1089,8 +1221,8 @@ setupvals(char *cmd, char *runscript, char *zsh_name)
#ifdef USE_GETPWUID
if ((pswd = getpwuid(cached_uid))) {
if (EMULATION(EMULATE_ZSH))
- home = metafy(pswd->pw_dir, -1, META_DUP);
- cached_username = ztrdup(pswd->pw_name);
+ home = ztrdup_metafy(pswd->pw_dir);
+ cached_username = ztrdup_metafy(pswd->pw_name);
}
else
#endif /* USE_GETPWUID */
@@ -1119,7 +1251,11 @@ setupvals(char *cmd, char *runscript, char *zsh_name)
pwd = metafy(zgetcwd(), -1, META_DUP);
}
- oldpwd = ztrdup(pwd); /* initialize `OLDPWD' = `PWD' */
+ oldpwd = zgetenv("OLDPWD");
+ if (oldpwd == NULL)
+ oldpwd = ztrdup(pwd); /* initialize `OLDPWD' = `PWD' */
+ else
+ oldpwd = ztrdup(oldpwd);
inittyptab(); /* initialize the ztypes table */
initlextabs(); /* initialize lexing tables */
@@ -1139,7 +1275,7 @@ setupvals(char *cmd, char *runscript, char *zsh_name)
adjustwinsize(0);
#else
/* columns and lines are normally zero, unless something different *
- * was inhereted from the environment. If either of them are zero *
+ * was inherited from the environment. If either of them are zero *
* the setiparam calls below set them to the defaults from termcap */
setiparam("COLUMNS", zterm_columns);
setiparam("LINES", zterm_lines);
@@ -1153,7 +1289,7 @@ setupvals(char *cmd, char *runscript, char *zsh_name)
#endif
breaks = loops = 0;
- lastmailcheck = time(NULL);
+ lastmailcheck = zmonotime(NULL);
locallevel = sourcelevel = 0;
sfcontext = SFC_NONE;
trap_return = 0;
@@ -1175,6 +1311,18 @@ setupvals(char *cmd, char *runscript, char *zsh_name)
/* Colour sequences for outputting colours in prompts and zle */
set_default_colour_sequences();
+ /* ZSH_EXEPATH */
+ {
+ char *mypath, *exename, *cwd;
+ exename = unmetafy(ztrdup(argv0), NULL);
+ cwd = pwd ? unmetafy(ztrdup(pwd), NULL) : NULL;
+ mypath = getmypath(exename, cwd);
+ free(exename);
+ free(cwd);
+ if (mypath) {
+ setsparam("ZSH_EXEPATH", metafy(mypath, -1, META_REALLOC));
+ }
+ }
if (cmd)
setsparam("ZSH_EXECUTION_STRING", ztrdup(cmd));
if (runscript)
@@ -1244,6 +1392,11 @@ setupshin(char *runscript)
void
init_signals(void)
{
+ struct sigaction act;
+
+ sigtrapped = (int *) hcalloc(TRAPCOUNT * sizeof(int));
+ siglists = (Eprog *) hcalloc(TRAPCOUNT * sizeof(Eprog));
+
if (interact) {
int i;
signal_setmask(signal_mask(0));
@@ -1254,14 +1407,8 @@ init_signals(void)
intr();
-#ifdef POSIX_SIGNALS
- {
- struct sigaction act;
- if (!sigaction(SIGQUIT, NULL, &act) &&
- act.sa_handler == SIG_IGN)
- sigtrapped[SIGQUIT] = ZSIG_IGNORED;
- }
-#endif
+ if (!sigaction(SIGQUIT, NULL, &act) && act.sa_handler == SIG_IGN)
+ sigtrapped[SIGQUIT] = ZSIG_IGNORED;
#ifndef QDEBUG
signal_ignore(SIGQUIT);
@@ -1654,8 +1801,7 @@ VA_DCL
lp = va_arg(ap, char **);
- pptbuf = unmetafy(promptexpand(lp ? *lp : NULL, 0, NULL, NULL,
- NULL),
+ pptbuf = unmetafy(promptexpand(lp ? *lp : NULL, 0, NULL, NULL),
&pptlen);
write_loop(2, pptbuf, pptlen);
free(pptbuf);
@@ -1711,7 +1857,7 @@ zsh_main(UNUSED(int argc), char **argv)
{
char **t, *runscript = NULL, *zsh_name;
char *cmd; /* argument to -c */
- int t0, needkeymap = 0;
+ int t0;
#ifdef USE_LOCALE
setlocale(LC_ALL, "");
#endif
@@ -1724,9 +1870,9 @@ zsh_main(UNUSED(int argc), char **argv)
* interactive
*/
typtab['\0'] |= IMETA;
- typtab[STOUC(Meta) ] |= IMETA;
- typtab[STOUC(Marker)] |= IMETA;
- for (t0 = (int)STOUC(Pound); t0 <= (int)STOUC(Nularg); t0++)
+ typtab[(unsigned char) Meta ] |= IMETA;
+ typtab[(unsigned char) Marker] |= IMETA;
+ for (t0 = (int) (unsigned char) Pound; t0 <= (int) (unsigned char) Nularg; t0++)
typtab[t0] |= ITOK | IMETA;
for (t = argv; *t; *t = metafy(*t, -1, META_ALLOC), t++);
@@ -1757,7 +1903,7 @@ zsh_main(UNUSED(int argc), char **argv)
createoptiontable();
/* sets emulation, LOGINSHELL, PRIVILEGED, ZLE, INTERACTIVE,
* SHINSTDIN and SINGLECOMMAND */
- parseargs(zsh_name, argv, &runscript, &cmd, &needkeymap);
+ parseargs(zsh_name, argv, &runscript, &cmd);
SHTTY = -1;
init_io(cmd);
@@ -1766,15 +1912,6 @@ zsh_main(UNUSED(int argc), char **argv)
init_signals();
init_bltinmods();
init_builtins();
-
- if (needkeymap)
- {
- /* Saved for after module system initialisation */
- zleentry(ZLE_CMD_SET_KEYMAP, needkeymap);
- opts[needkeymap] = 1;
- opts[needkeymap == EMACSMODE ? VIMODE : EMACSMODE] = 0;
- }
-
run_init_scripts();
setupshin(runscript);
init_misc(cmd, zsh_name);
diff --git a/Src/input.c b/Src/input.c
index 9898a7177..320fd6500 100644
--- a/Src/input.c
+++ b/Src/input.c
@@ -217,32 +217,29 @@ shinbufrestore(void)
static int
shingetchar(void)
{
- int nread, rsize = isset(SHINSTDIN) ? 1 : SHINBUFSIZE;
+ int nread;
if (shinbufptr < shinbufendptr)
- return STOUC(*shinbufptr++);
+ return (unsigned char) *shinbufptr++;
shinbufreset();
#ifdef USE_LSEEK
- if (rsize == 1 && lseek(SHIN, 0, SEEK_CUR) != (off_t)-1)
- rsize = SHINBUFSIZE;
- if (rsize > 1) {
+ if (!isset(SHINSTDIN) || lseek(SHIN, 0, SEEK_CUR) != (off_t) -1) {
do {
errno = 0;
- nread = read(SHIN, shinbuffer, rsize);
+ nread = read(SHIN, shinbuffer, SHINBUFSIZE);
} while (nread < 0 && errno == EINTR);
if (nread <= 0)
return -1;
if (isset(SHINSTDIN) &&
(shinbufendptr = memchr(shinbuffer, '\n', nread))) {
- shinbufendptr++;
- rsize = (shinbufendptr - shinbuffer);
+ int rsize = (++shinbufendptr - shinbuffer);
if (nread > rsize &&
lseek(SHIN, -(nread - rsize), SEEK_CUR) < 0)
zerr("lseek(%d, %d): %e", SHIN, -(nread - rsize), errno);
} else
shinbufendptr = shinbuffer + nread;
- return STOUC(*shinbufptr++);
+ return (unsigned char) *shinbufptr++;
}
#endif
for (;;) {
@@ -259,7 +256,7 @@ shingetchar(void)
}
if (shinbufendptr == shinbuffer)
return -1;
- return STOUC(*shinbufptr++);
+ return (unsigned char) *shinbufptr++;
}
/* Read a line from SHIN. Convert tokens and *
@@ -328,7 +325,7 @@ ingetc(void)
if (inbufleft) {
inbufleft--;
inbufct--;
- if (itok(lastc = STOUC(*inbufptr++)))
+ if (itok(lastc = (unsigned char) *inbufptr++))
continue;
if (((inbufflags & INP_LINENO) || !strin) && lastc == '\n')
lineno++;
@@ -402,7 +399,7 @@ inputline(void)
char *pptbuf;
int pptlen;
pptbuf = unmetafy(promptexpand(ingetcpmptl ? *ingetcpmptl : NULL,
- 0, NULL, NULL, NULL), &pptlen);
+ 0, NULL, NULL), &pptlen);
write_loop(2, pptbuf, pptlen);
free(pptbuf);
}
@@ -610,11 +607,11 @@ inungetc(int c)
}
}
-/* stuff a whole file into the input queue and print it */
+/* stuff a whole file into memory and return it */
/**/
-int
-stuff(char *fn)
+off_t
+zstuff(char **out, const char *fn)
{
FILE *in;
char *buf;
@@ -622,20 +619,39 @@ stuff(char *fn)
if (!(in = fopen(unmeta(fn), "r"))) {
zerr("can't open %s", fn);
- return 1;
+ return -1;
}
+ queue_signals();
fseek(in, 0, SEEK_END);
len = ftell(in);
fseek(in, 0, SEEK_SET);
buf = (char *)zalloc(len + 1);
- if (!(fread(buf, len, 1, in))) {
+ if (len && !(fread(buf, len, 1, in))) {
zerr("read error on %s", fn);
fclose(in);
zfree(buf, len + 1);
- return 1;
+ unqueue_signals();
+ return -1;
}
fclose(in);
buf[len] = '\0';
+ *out = buf;
+ unqueue_signals();
+ return len;
+}
+
+/* stuff a whole file into the input queue and print it */
+
+/**/
+int
+stuff(char *fn)
+{
+ char *buf;
+ off_t len = zstuff(&buf, fn);
+
+ if (len < 0)
+ return 1;
+
fwrite(buf, len, 1, stderr);
fflush(stderr);
inputsetline(metafy(buf, len, META_REALLOC), INP_FREE);
@@ -816,6 +832,7 @@ char *input_hasalias(void)
{
int flags = inbufflags;
struct instacks *instackptr = instacktop;
+ char *alias_nam = NULL;
for (;;)
{
@@ -824,9 +841,9 @@ char *input_hasalias(void)
DPUTS(instackptr == instack, "BUG: continuation at bottom of instack");
instackptr--;
if (instackptr->alias)
- return instackptr->alias->node.nam;
+ alias_nam = instackptr->alias->node.nam;
flags = instackptr->flags;
}
- return NULL;
+ return alias_nam;
}
diff --git a/Src/jobs.c b/Src/jobs.c
index a91ef787f..ad14f6312 100644
--- a/Src/jobs.c
+++ b/Src/jobs.c
@@ -136,7 +136,7 @@ int numpipestats, pipestats[MAX_PIPESTATS];
/**/
static struct timeval *
-dtime(struct timeval *dt, struct timeval *t1, struct timeval *t2)
+dtime_tv(struct timeval *dt, struct timeval *t1, struct timeval *t2)
{
dt->tv_sec = t2->tv_sec - t1->tv_sec;
dt->tv_usec = t2->tv_usec - t1->tv_usec;
@@ -147,6 +147,21 @@ dtime(struct timeval *dt, struct timeval *t1, struct timeval *t2)
return dt;
}
+/* As above, but with timespecs */
+
+/**/
+static struct timespec *
+dtime_ts(struct timespec *dt, struct timespec *t1, struct timespec *t2)
+{
+ dt->tv_sec = t2->tv_sec - t1->tv_sec;
+ dt->tv_nsec = t2->tv_nsec - t1->tv_nsec;
+ if (dt->tv_nsec < 0) {
+ dt->tv_nsec += 1000000000.0;
+ dt->tv_sec -= 1.0;
+ }
+ return dt;
+}
+
/* change job table entry from stopped to running */
/**/
@@ -349,7 +364,6 @@ get_usage(void)
void
update_process(Process pn, int status)
{
- struct timezone dummy_tz;
#ifdef HAVE_GETRUSAGE
struct timeval childs = child_usage.ru_stime;
struct timeval childu = child_usage.ru_utime;
@@ -360,12 +374,12 @@ update_process(Process pn, int status)
/* get time-accounting info */
get_usage();
- gettimeofday(&pn->endtime, &dummy_tz); /* record time process exited */
+ zgettime_monotonic_if_available(&pn->endtime); /* record time process exited */
pn->status = status; /* save the status returned by WAIT */
#ifdef HAVE_GETRUSAGE
- dtime(&pn->ti.ru_stime, &childs, &child_usage.ru_stime);
- dtime(&pn->ti.ru_utime, &childu, &child_usage.ru_utime);
+ dtime_tv(&pn->ti.ru_stime, &childs, &child_usage.ru_stime);
+ dtime_tv(&pn->ti.ru_utime, &childu, &child_usage.ru_utime);
#else
pn->ti.st = shtms.tms_cstime - childs; /* compute process system space time */
pn->ti.ut = shtms.tms_cutime - childu; /* compute process user space time */
@@ -414,7 +428,7 @@ storepipestats(Job jn, int inforeground, int fixlastval)
jpipestats[i] = (WIFSIGNALED(p->status) ?
0200 | WTERMSIG(p->status) :
(WIFSTOPPED(p->status) ?
- 0200 | WEXITSTATUS(p->status) :
+ 0200 | WSTOPSIG(p->status) :
WEXITSTATUS(p->status)));
if (jpipestats[i])
pipefail = jpipestats[i];
@@ -427,11 +441,17 @@ storepipestats(Job jn, int inforeground, int fixlastval)
}
if (fixlastval) {
- if (jn->stat & STAT_CURSH) {
- if (!lastval && isset(PIPEFAIL))
- lastval = pipefail;
- } else if (isset(PIPEFAIL))
- lastval = pipefail;
+ if (jn->stat & STAT_CURSH) {
+ if (!lastval && isset(PIPEFAIL)) {
+ if (inforeground)
+ this_noerrexit = 0;
+ lastval = pipefail;
+ }
+ } else if (isset(PIPEFAIL)) {
+ if (inforeground)
+ this_noerrexit = 0;
+ lastval = pipefail;
+ }
}
}
@@ -471,7 +491,7 @@ update_job(Job jn)
val = (WIFSIGNALED(pn->status) ?
0200 | WTERMSIG(pn->status) :
(WIFSTOPPED(pn->status) ?
- 0200 | WEXITSTATUS(pn->status) :
+ 0200 | WSTOPSIG(pn->status) :
WEXITSTATUS(pn->status)));
signalled = WIFSIGNALED(pn->status);
}
@@ -544,16 +564,14 @@ update_job(Job jn)
if (isset(MONITOR)) {
pid_t pgrp = gettygrp(); /* get process group of tty */
+ int deadpgrp = (mypgrp != pgrp && inforeground && pgrp > 1 &&
+ kill(-pgrp, 0) == -1 && errno == ESRCH);
/* is this job in the foreground of an interactive shell? */
if (mypgrp != pgrp && inforeground &&
- (jn->gleader == pgrp ||
- (pgrp > 1 &&
- (kill(-pgrp, 0) == -1 && errno == ESRCH)))) {
+ ((jn->gleader == pgrp && signalled) || deadpgrp)) {
if (list_pipe) {
- if (somestopped || (pgrp > 1 &&
- kill(-pgrp, 0) == -1 &&
- errno == ESRCH)) {
+ if (somestopped || deadpgrp) {
attachtty(mypgrp);
/* check window size and adjust if necessary */
adjustwinsize(0);
@@ -656,6 +674,25 @@ update_job(Job jn)
}
}
+/**/
+void
+update_bg_job(Job jn, pid_t pid, int status)
+{
+ /*
+ * Accumulate a list of older jobs. We only do this for
+ * background jobs, which is something in the job table
+ * that's not marked as in the current shell or as shell builtin
+ * and is not equal to the current foreground job.
+ */
+ if (jn && !(jn->stat & (STAT_CURSH|STAT_BUILTIN)) &&
+ jn - jobtab != thisjob) {
+ if (WIFEXITED(status))
+ addbgstatus(pid, WEXITSTATUS(status));
+ else if (WIFSIGNALED(status))
+ addbgstatus(pid, 0200 | WTERMSIG(status));
+ }
+}
+
/* set the previous job to something reasonable */
/**/
@@ -730,7 +767,7 @@ printhhmmss(double secs)
}
static void
-printtime(struct timeval *real, child_times_t *ti, char *desc)
+printtime(struct timespec *real, child_times_t *ti, char *desc)
{
char *s;
double elapsed_time, user_time, system_time;
@@ -751,21 +788,21 @@ printtime(struct timeval *real, child_times_t *ti, char *desc)
}
/* go ahead and compute these, since almost every TIMEFMT will have them */
- elapsed_time = real->tv_sec + real->tv_usec / 1000000.0;
+ elapsed_time = real->tv_sec + real->tv_nsec / 1000000000.0;
#ifdef HAVE_GETRUSAGE
user_time = ti->ru_utime.tv_sec + ti->ru_utime.tv_usec / 1000000.0;
system_time = ti->ru_stime.tv_sec + ti->ru_stime.tv_usec / 1000000.0;
total_time = user_time + system_time;
percent = 100.0 * total_time
- / (real->tv_sec + real->tv_usec / 1000000.0);
+ / (real->tv_sec + real->tv_nsec / 1000000000.0);
#else
{
long clktck = get_clktck();
user_time = ti->ut / (double) clktck;
system_time = ti->st / (double) clktck;
percent = 100.0 * (ti->ut + ti->st)
- / (clktck * real->tv_sec + clktck * real->tv_usec / 1000000.0);
+ / (clktck * real->tv_sec + clktck * real->tv_nsec / 1000000000.0);
}
#endif
@@ -821,6 +858,23 @@ printtime(struct timeval *real, child_times_t *ti, char *desc)
break;
}
break;
+ case 'n':
+ switch (*++s) {
+ case 'E':
+ fprintf(stderr, "%0.fns", elapsed_time * 1000000000.0);
+ break;
+ case 'U':
+ fprintf(stderr, "%0.fns", user_time * 1000000000.0);
+ break;
+ case 'S':
+ fprintf(stderr, "%0.fns", system_time * 1000000000.0);
+ break;
+ default:
+ fprintf(stderr, "%%n");
+ s--;
+ break;
+ }
+ break;
case '*':
switch (*++s) {
case 'E':
@@ -890,8 +944,13 @@ printtime(struct timeval *real, child_times_t *ti, char *desc)
break;
#endif
#ifdef HAVE_STRUCT_RUSAGE_RU_MAXRSS
+#ifdef RU_MAXRSS_IS_IN_BYTES
+# define MAXRSS_IN_KB(x) ((x)/1024)
+#else
+# define MAXRSS_IN_KB(x) (x)
+#endif
case 'M':
- fprintf(stderr, "%ld", ti->ru_maxrss / 1024);
+ fprintf(stderr, "%ld", MAXRSS_IN_KB(ti->ru_maxrss));
break;
#endif
#ifdef HAVE_STRUCT_RUSAGE_RU_MAJFLT
@@ -963,12 +1022,12 @@ static void
dumptime(Job jn)
{
Process pn;
- struct timeval dtimeval;
+ struct timespec dtimespec;
if (!jn->procs)
return;
for (pn = jn->procs; pn; pn = pn->next)
- printtime(dtime(&dtimeval, &pn->bgtime, &pn->endtime), &pn->ti,
+ printtime(dtime_ts(&dtimespec, &pn->bgtime, &pn->endtime), &pn->ti,
pn->text);
}
@@ -1029,7 +1088,7 @@ should_report_time(Job j)
return 1;
#else
{
- clktck = get_clktck();
+ long clktck = get_clktck();
if ((j->procs->ti.ut + j->procs->ti.st) / clktck >= reporttime)
return 1;
}
@@ -1038,13 +1097,28 @@ should_report_time(Job j)
#ifdef HAVE_GETRUSAGE
if (reportmemory >= 0 &&
- j->procs->ti.ru_maxrss / 1024 > reportmemory)
+ MAXRSS_IN_KB(j->procs->ti.ru_maxrss) > reportmemory)
return 1;
#endif
return 0;
}
+/**/
+char *
+sigmsg(int sig)
+{
+ static char *unknown = "unknown signal";
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+ static char rtmsg[] = "real-time event XXX";
+ if (sig >= SIGRTMIN && sig <= SIGRTMAX) {
+ sprintf(rtmsg + sizeof(rtmsg) - 4, "%u", sig - SIGRTMIN + 1);
+ return rtmsg;
+ }
+#endif
+ return sig <= SIGCOUNT ? sig_msg[sig] : unknown;
+}
+
/* !(lng & 3) means jobs *
* (lng & 1) means jobs -l *
* (lng & 2) means jobs -p
@@ -1374,8 +1448,10 @@ cleanfilelists(void)
DPUTS(shell_exiting >= 0, "BUG: cleanfilelists() before exit");
- for (i = 1; i <= maxjob; i++)
+ for (i = 1; i <= maxjob; i++) {
deletefilelist(jobtab[i].filelist, 0);
+ jobtab[i].filelist = 0;
+ }
}
/**/
@@ -1461,7 +1537,7 @@ deletejob(Job jn, int disowning)
/**/
void
-addproc(pid_t pid, char *text, int aux, struct timeval *bgtime,
+addproc(pid_t pid, char *text, int aux, struct timespec *bgtime,
int gleader, int list_pipe_job_used)
{
Process pn, *pnlist;
@@ -1488,10 +1564,7 @@ addproc(pid_t pid, char *text, int aux, struct timeval *bgtime,
* set it for that, too.
*/
if (gleader != -1) {
- if (jobtab[thisjob].stat & STAT_CURSH)
- jobtab[thisjob].gleader = gleader;
- else
- jobtab[thisjob].gleader = pid;
+ jobtab[thisjob].gleader = gleader;
if (list_pipe_job_used != -1)
jobtab[list_pipe_job_used].gleader = gleader;
/*
@@ -1500,7 +1573,7 @@ addproc(pid_t pid, char *text, int aux, struct timeval *bgtime,
*/
last_attached_pgrp = gleader;
} else if (!jobtab[thisjob].gleader)
- jobtab[thisjob].gleader = pid;
+ jobtab[thisjob].gleader = pid;
/* attach this process to end of process list of current job */
pnlist = &jobtab[thisjob].procs;
}
@@ -1536,8 +1609,10 @@ havefiles(void)
int i;
for (i = 1; i <= maxjob; i++)
- if (jobtab[i].stat && jobtab[i].filelist)
+ if (jobtab[i].stat && jobtab[i].filelist &&
+ peekfirst(jobtab[i].filelist)) {
return 1;
+ }
return 0;
}
@@ -1850,16 +1925,15 @@ spawnjob(void)
/**/
void
-shelltime(void)
+shelltime(child_times_t *shell, child_times_t *kids, struct timespec *then, int delta)
{
- struct timezone dummy_tz;
- struct timeval dtimeval, now;
+ struct timespec dtimespec, now;
child_times_t ti;
#ifndef HAVE_GETRUSAGE
struct tms buf;
#endif
- gettimeofday(&now, &dummy_tz);
+ zgettime_monotonic_if_available(&now);
#ifdef HAVE_GETRUSAGE
getrusage(RUSAGE_SELF, &ti);
@@ -1869,7 +1943,28 @@ shelltime(void)
ti.ut = buf.tms_utime;
ti.st = buf.tms_stime;
#endif
- printtime(dtime(&dtimeval, &shtimer, &now), &ti, "shell");
+ if (shell) {
+ if (delta) {
+#ifdef HAVE_GETRUSAGE
+ dtime_tv(&ti.ru_utime, &shell->ru_utime, &ti.ru_utime);
+ dtime_tv(&ti.ru_stime, &shell->ru_stime, &ti.ru_stime);
+#else
+ ti.ut -= shell->ut;
+ ti.st -= shell->st;
+#endif
+ } else
+ *shell = ti;
+ }
+ if (delta)
+ dtime_ts(&dtimespec, then, &now);
+ else {
+ if (then)
+ *then = now;
+ dtime_ts(&dtimespec, &shtimer, &now);
+ }
+
+ if (!delta == !shell)
+ printtime(&dtimespec, &ti, "shell");
#ifdef HAVE_GETRUSAGE
getrusage(RUSAGE_CHILDREN, &ti);
@@ -1877,8 +1972,20 @@ shelltime(void)
ti.ut = buf.tms_cutime;
ti.st = buf.tms_cstime;
#endif
- printtime(&dtimeval, &ti, "children");
-
+ if (kids) {
+ if (delta) {
+#ifdef HAVE_GETRUSAGE
+ dtime_tv(&ti.ru_utime, &kids->ru_utime, &ti.ru_utime);
+ dtime_tv(&ti.ru_stime, &kids->ru_stime, &ti.ru_stime);
+#else
+ ti.ut -= shell->ut;
+ ti.st -= shell->st;
+#endif
+ } else
+ *kids = ti;
+ }
+ if (!delta == !kids)
+ printtime(&dtimespec, &ti, "children");
}
/* see if jobs need printing */
@@ -2221,6 +2328,9 @@ addbgstatus(pid_t pid, int status)
{
static long child_max;
Bgstatus bgstatus_entry;
+#ifdef DEBUG
+ LinkNode node;
+#endif
if (!child_max) {
#ifdef _SC_CHILD_MAX
@@ -2244,6 +2354,21 @@ addbgstatus(pid_t pid, int status)
if (!bgstatus_list)
return;
}
+#ifdef DEBUG
+ /* See if an entry already exists for the pid */
+ for (node = firstnode(bgstatus_list); node; incnode(node)) {
+ bgstatus_entry = (Bgstatus)getdata(node);
+ if (bgstatus_entry->pid == pid) {
+ /* In theory this should not happen because addbgstatus() is
+ * called only once when the process exits or gets killed. */
+ dputs("addbgstatus called again: pid %d: status %d -> %d",
+ pid, bgstatus_entry->status, status);
+ bgstatus_entry->status = status;
+ return;
+ }
+ }
+#endif
+ /* Add an entry for the pid */
if (bgstatus_count == child_max) {
/* Overflow. List is in order, remove first */
rembgstatus(firstnode(bgstatus_list));
@@ -2384,7 +2509,7 @@ bin_fg(char *name, char **argv, Options ops, int func)
int curmaxjob, ignorejob;
if (unset(MONITOR) && oldmaxjob) {
jobptr = oldjobtab;
- curmaxjob = oldmaxjob ? oldmaxjob - 1 : 0;
+ curmaxjob = oldmaxjob;
ignorejob = 0;
} else {
jobptr = jobtab;
@@ -2488,6 +2613,7 @@ bin_fg(char *name, char **argv, Options ops, int func)
jobtab[job].stat &= ~STAT_CURSH;
}
if ((stopped = (jobtab[job].stat & STAT_STOPPED))) {
+ /* WIFCONTINUED will makerunning() again at killjb() */
makerunning(jobtab + job);
if (func == BIN_BG) {
/* Set $! to indicate this was backgrounded */
@@ -2646,11 +2772,37 @@ static const struct {
int
bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func))
{
- int sig = SIGTERM;
+ int status, sig = SIGTERM;
int returnval = 0;
+#ifdef HAVE_SIGQUEUE
+ union sigval sigqueue_info;
+#endif
+ int use_sigqueue = 0, got_sig = 0;
+
+ while (*argv && **argv == '-') {
+ if (!use_sigqueue && (*argv)[1] == 'q' && (*argv)[2] == '\0') {
+ char *endp;
+
+ if (!*++argv) {
+ zwarnnam(nam, "-q: argument expected");
+ return 1;
+ }
+#ifdef HAVE_SIGQUEUE
+ sigqueue_info.sival_int =
+#endif
+ zstrtol(*argv, &endp, 10);
+ if (*endp) {
+ zwarnnam(nam, "invalid number: %s", *argv);
+ return 1;
+ }
+ use_sigqueue = 1;
+ argv++;
+ continue;
+ }
+ if (got_sig)
+ break;
/* check for, and interpret, a signal specifier */
- if (*argv && **argv == '-') {
if (idigit((*argv)[1])) {
char *endp;
/* signal specified by number */
@@ -2666,23 +2818,29 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func))
if ((*argv)[1] == 'l' && (*argv)[2] == '\0') {
if (argv[1]) {
while (*++argv) {
- sig = zstrtol(*argv, &signame, 10);
+ status = zstrtol(*argv, &signame, 10);
if (signame == *argv) {
+ signame = casemodify(signame, CASMOD_UPPER);
if (!strncmp(signame, "SIG", 3))
signame += 3;
for (sig = 1; sig <= SIGCOUNT; sig++)
- if (!strcasecmp(sigs[sig], signame))
+ if (!strcmp(sigs[sig], signame))
break;
if (sig > SIGCOUNT) {
int i;
for (i = 0; alt_sigs[i].name; i++)
- if (!strcasecmp(alt_sigs[i].name, signame))
+ if (!strcmp(alt_sigs[i].name, signame))
{
sig = alt_sigs[i].num;
break;
}
}
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+ if (sig > SIGCOUNT && (sig = rtsigno(signame))) {
+ printf("%d\n", sig);
+ } else
+#endif
if (sig > SIGCOUNT) {
zwarnnam(nam, "unknown signal: SIG%s",
signame);
@@ -2695,14 +2853,15 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func))
signame);
returnval++;
} else {
- if (WIFSIGNALED(sig))
- sig = WTERMSIG(sig);
- else if (WIFSTOPPED(sig))
- sig = WSTOPSIG(sig);
+ sig = status & ~0200;
if (1 <= sig && sig <= SIGCOUNT)
printf("%s\n", sigs[sig]);
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+ else if ((signame = rtsigname(sig, 0)))
+ printf("%s\n", signame);
+#endif
else
- printf("%d\n", sig);
+ printf("%d\n", status);
}
}
}
@@ -2711,10 +2870,45 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func))
printf("%s", sigs[1]);
for (sig = 2; sig <= SIGCOUNT; sig++)
printf(" %s", sigs[sig]);
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+ for (sig = SIGRTMIN; sig <= SIGRTMAX; sig++)
+ printf(" %s", rtsigname(sig, 0));
+#endif
putchar('\n');
return 0;
}
+ /* with argument "-L" list signals with their numbers in a table */
+ if ((*argv)[1] == 'L' && (*argv)[2] == '\0') {
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+ const int width = SIGRTMAX >= 100 ? 3 : 2;
+#else
+ const int width = SIGCOUNT >= 100 ? 3 : 2;
+#endif
+ const int cols = zterm_columns >= 30 ?
+ (zterm_columns < 90 ? zterm_columns / 15 : 6) : 1;
+
+ for (sig = 1; sig < SIGCOUNT
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+ + 1
+#endif
+ ; sig++)
+ {
+ printf("%*d %-10s%c", width, sig, sigs[sig],
+ sig % cols ? ' ' : '\n');
+ }
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+ for (sig = SIGRTMIN; sig < SIGRTMAX; sig++) {
+ printf("%*d %-10s%c", width, sig, rtsigname(sig, 0),
+ (sig - SIGRTMIN + SIGCOUNT + 1) % cols ? ' ' : '\n');
+ }
+ printf("%*d RTMAX\n", width, sig);
+#else
+ printf("%*d %s\n", width, sig, sigs[sig]);
+#endif
+ return 0;
+ }
+
if ((*argv)[1] == 'n' && (*argv)[2] == '\0') {
char *endp;
@@ -2759,14 +2953,19 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func))
break;
}
}
- if (sig > SIGCOUNT) {
+ if (sig > SIGCOUNT
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+ && !(sig = rtsigno(signame))
+#endif
+ ) {
zwarnnam(nam, "unknown signal: SIG%s", signame);
- zwarnnam(nam, "type kill -l for a list of signals");
+ zwarnnam(nam, "type kill -L for a list of signals");
return 1;
}
}
}
argv++;
+ got_sig = 1;
}
/* Discard the standard "-" and "--" option breaks */
@@ -2815,7 +3014,12 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func))
returnval++;
} else {
int pid = atoi(*argv);
- if (kill(pid, sig) == -1) {
+ if (
+#ifdef HAVE_SIGQUEUE
+ use_sigqueue ? sigqueue(pid, sig, sigqueue_info) :
+#endif
+ kill(pid, sig) == -1)
+ {
zwarnnam("kill", "kill %s failed: %e", *argv, errno);
returnval++;
}
@@ -2836,18 +3040,19 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func))
return returnval < 126 ? returnval : 1;
}
-/* Get a signal number from a string */
+
+/* Get index into table of traps from a string describing a signal */
/**/
mod_export int
-getsignum(const char *s)
+getsigidx(const char *s)
{
int x, i;
/* check for a signal specified by number */
x = atoi(s);
- if (idigit(*s) && x >= 0 && x < VSIGCOUNT)
- return x;
+ if (idigit(*s) && x >= 0)
+ return SIGIDX(x);
/* search for signal by name */
if (!strncmp(s, "SIG", 3))
@@ -2863,11 +3068,16 @@ getsignum(const char *s)
return alt_sigs[i].num;
}
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+ if ((x = rtsigno(s)))
+ return SIGIDX(x);
+#endif
+
/* no matching signal */
return -1;
}
-/* Get the name for a signal. */
+/* Get the name for a signal given the index into the traps table. */
/**/
mod_export const char *
@@ -2881,6 +3091,11 @@ getsigname(int sig)
return alt_sigs[i].name;
}
else
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+ if (sig >= VSIGCOUNT)
+ return rtsigname(SIGNUM(sig), 0);
+ else
+#endif
return sigs[sig];
/* shouldn't reach here */
@@ -2905,10 +3120,22 @@ gettrapnode(int sig, int ignoredisable)
else
getptr = shfunctab->getnode;
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+ if (sig >= VSIGCOUNT)
+ sprintf(fname, "TRAP%s", rtsigname(SIGNUM(sig), 0));
+ else
+#endif
sprintf(fname, "TRAP%s", sigs[sig]);
if ((hn = getptr(shfunctab, fname)))
return hn;
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+ if (sig >= VSIGCOUNT) {
+ sprintf(fname, "TRAP%s", rtsigname(SIGNUM(sig), 1));
+ return getptr(shfunctab, fname);
+ }
+#endif
+
for (i = 0; alt_sigs[i].name; i++) {
if (alt_sigs[i].num == sig) {
sprintf(fname, "TRAP%s", alt_sigs[i].name);
diff --git a/Src/lex.c b/Src/lex.c
index ece02659e..efbb62b66 100644
--- a/Src/lex.c
+++ b/Src/lex.c
@@ -423,7 +423,7 @@ initlextabs(void)
for (t0 = 0; lx2[t0]; t0++)
lexact2[(int)lx2[t0]] = t0;
lexact2['&'] = LX2_BREAK;
- lexact2[STOUC(Meta)] = LX2_META;
+ lexact2[(unsigned char) Meta] = LX2_META;
lextok2['*'] = Star;
lextok2['?'] = Quest;
lextok2['{'] = Inbrace;
@@ -722,7 +722,7 @@ gettok(void)
}
return peek;
}
- switch (lexact1[STOUC(c)]) {
+ switch (lexact1[(unsigned char) c]) {
case LX1_BKSLASH:
d = hgetc();
if (d == '\n')
@@ -937,8 +937,8 @@ static enum lextok
gettokstr(int c, int sub)
{
int bct = 0, pct = 0, brct = 0, seen_brct = 0, fdpar = 0;
- int intpos = 1, in_brace_param = 0;
- int inquote, unmatched = 0;
+ int intpos = 1, in_brace_param = 0, cmdsubst = 0;
+ int inquote, unmatched = 0, in_pattern = 0;
enum lextok peek;
#ifdef DEBUG
int ocmdsp = cmdsp;
@@ -960,8 +960,8 @@ gettokstr(int c, int sub)
if (inbl && !in_brace_param && !pct)
act = LX2_BREAK;
else {
- act = lexact2[STOUC(c)];
- c = lextok2[STOUC(c)];
+ act = lexact2[(unsigned char) c];
+ c = lextok2[(unsigned char) c];
}
switch (act) {
case LX2_BREAK:
@@ -1135,7 +1135,7 @@ gettokstr(int c, int sub)
c = Inpar;
break;
case LX2_INBRACE:
- if (isset(IGNOREBRACES) || sub)
+ if ((isset(IGNOREBRACES) && !cmdsubst) || sub)
c = '{';
else {
if (!lexbuf.len && incmdpos) {
@@ -1157,8 +1157,11 @@ gettokstr(int c, int sub)
if (in_brace_param) {
cmdpop();
}
- if (bct-- == in_brace_param)
- in_brace_param = 0;
+ if (bct-- == in_brace_param) {
+ if (cmdsubst)
+ cmdpop();
+ in_brace_param = cmdsubst = in_pattern = 0;
+ }
c = Outbrace;
break;
case LX2_COMMA:
@@ -1230,7 +1233,7 @@ gettokstr(int c, int sub)
else {
int sav = *lexbuf.ptr;
*lexbuf.ptr = '\0';
- t = itype_end(t, IIDENT, 0);
+ t = itype_end(t, INAMESPC, 0);
if (t < lexbuf.ptr) {
skipparens(Inbrack, Outbrack, &t);
} else {
@@ -1263,7 +1266,7 @@ gettokstr(int c, int sub)
continue;
} else {
add(Bnull);
- if (c == STOUC(Meta)) {
+ if (c == (unsigned char) Meta) {
c = hgetc();
#ifdef DEBUG
if (lexstop) {
@@ -1306,7 +1309,8 @@ gettokstr(int c, int sub)
lexbuf.ptr--, lexbuf.len--;
else
break;
- }
+ } else if (in_pattern && c == '/')
+ add(Bnull);
add(c);
}
ALLOWHIST
@@ -1394,27 +1398,46 @@ gettokstr(int c, int sub)
*/
c = Dash;
break;
- case LX2_BANG:
- /*
- * Same logic as Dash, for ! to perform negation in range.
- */
- if (seen_brct)
- c = Bang;
- else
- c = '!';
- }
- add(c);
- c = hgetc();
+ case LX2_BANG:
+ /*
+ * Same logic as Dash, for ! to perform negation in range.
+ */
+ if (seen_brct && brct)
+ c = Bang;
+ else
+ c = '!';
+ break;
+ case LX2_OTHER:
+ if (in_brace_param) {
+ if (c == '/') {
+ if (in_pattern == 0)
+ in_pattern = 2;
+ else
+ --in_pattern;
+ }
+ }
+ }
+ add(c);
+ c = hgetc();
if (intpos)
intpos--;
if (lexstop)
break;
+ if (!cmdsubst && in_brace_param && act == LX2_STRING &&
+ (c == '|' || c == Bar || c == '{' || c == Inbrace || inblank(c))) {
+ cmdsubst = in_brace_param;
+ cmdpush(CS_CURSH);
+ } else if (in_pattern == 2 && c != '/')
+ in_pattern = 1;
}
brk:
if (errflag) {
if (in_brace_param) {
- while(bct-- >= in_brace_param)
+ while(bct >= in_brace_param) {
+ if (bct-- == cmdsubst)
+ cmdpop();
cmdpop();
+ }
}
return LEXERR;
}
@@ -1422,17 +1445,28 @@ gettokstr(int c, int sub)
if (unmatched && !(lexflags & LEXFLAGS_ACTIVE))
zerr("unmatched %c", unmatched);
if (in_brace_param) {
- while(bct-- >= in_brace_param)
+ while(bct >= in_brace_param) {
+ if (bct-- == cmdsubst)
+ cmdpop();
cmdpop();
+ }
zerr("closing brace expected");
} else if (unset(IGNOREBRACES) && !sub && lexbuf.len > 1 &&
peek == STRING && lexbuf.ptr[-1] == '}' &&
lexbuf.ptr[-2] != Bnull) {
/* hack to get {foo} command syntax work */
+ /*
+ * Alias expansion when parsing command substitution means that
+ * the case for raw lexical analysis may not be the same.
+ * (Just go with it, OK?)
+ */
+ int lar = lex_add_raw;
+ lex_add_raw = lexbuf_raw.len > 0 && lexbuf_raw.ptr[-1] == '}';
lexbuf.ptr--;
lexbuf.len--;
lexstop = 0;
hungetc('}');
+ lex_add_raw = lar;
}
*lexbuf.ptr = '\0';
DPUTS(cmdsp != ocmdsp, "BUG: gettok: cmdstack changed.");
@@ -1451,8 +1485,8 @@ gettokstr(int c, int sub)
static int
dquote_parse(char endchar, int sub)
{
- int pct = 0, brct = 0, bct = 0, intick = 0, err = 0;
- int c;
+ int pct = 0, brct = 0, bct = 0, intick = 0, err = 0, cmdsubst = 0;
+ int c, bskip = 0;
int math = endchar == ')' || endchar == ']' || infor;
int zlemath = math && zlemetacs > zlemetall + addedx - inbufct;
@@ -1521,11 +1555,25 @@ dquote_parse(char endchar, int sub)
c = Qstring;
}
break;
+ case '{':
+ if (cmdsubst && !intick) {
+ /* In nofork substitution, tokenize as if unquoted */
+ c = Inbrace;
+ bskip++;
+ }
+ break;
case '}':
if (intick || !bct)
break;
c = Outbrace;
- bct--;
+ if (bskip) {
+ bskip--;
+ break;
+ }
+ if (bct-- == cmdsubst) {
+ cmdsubst = 0;
+ cmdpop();
+ }
cmdpop();
break;
case '`':
@@ -1580,14 +1628,34 @@ dquote_parse(char endchar, int sub)
if (err || lexstop)
break;
add(c);
+ if (!cmdsubst && c == Inbrace) {
+ /* Check for ${|...} nofork command substitution */
+ if ((c = hgetc()) && !lexstop) {
+ if (c == '|' || inblank(c)) {
+ cmdsubst = bct;
+ cmdpush(CS_CURSH);
+ }
+ hungetc(c);
+ }
+ }
}
if (intick == 2)
ALLOWHIST
if (intick) {
cmdpop();
}
- while (bct--)
+ while (bct) {
+ if (bct-- == cmdsubst) {
+ /*
+ * You would think this is an error, but if we call it one,
+ * parsestrnoerr() returns nonzero to subst_parse_str() and
+ * subsequently "bad substitution" is not reported
+ */
+ /* err = 1 */
+ cmdpop();
+ }
cmdpop();
+ }
if (lexstop)
err = intick || endchar || err;
else if (err == 1) {
diff --git a/Src/loop.c b/Src/loop.c
index db5b3e097..979285abc 100644
--- a/Src/loop.c
+++ b/Src/loop.c
@@ -53,7 +53,7 @@ execfor(Estate state, int do_exec)
wordcode code = state->pc[-1];
int iscond = (WC_FOR_TYPE(code) == WC_FOR_COND), ctok = 0, atok = 0;
int last = 0;
- char *name, *str, *cond = NULL, *advance = NULL;
+ char *str, *cond = NULL, *advance = NULL;
zlong val = 0;
LinkList vars = NULL, args = NULL;
int old_simple_pline = simple_pline;
@@ -151,7 +151,7 @@ execfor(Estate state, int do_exec)
int count = 0;
for (node = firstnode(vars); node; incnode(node))
{
- name = (char *)getdata(node);
+ char *name = (char *)getdata(node);
if (!args || !(str = (char *) ugetnode(args)))
{
if (count) {
@@ -165,7 +165,7 @@ execfor(Estate state, int do_exec)
fprintf(xtrerr, "%s=%s\n", name, str);
fflush(xtrerr);
}
- setsparam(name, ztrdup(str));
+ setloopvar(name, str);
count++;
}
if (!count)
@@ -282,7 +282,7 @@ execselect(Estate state, UNUSED(int do_exec))
/* Keep any user interrupt error status */
errflag = oef | (errflag & ERRFLAG_INT);
} else {
- str = promptexpand(prompt3, 0, NULL, NULL, NULL);
+ str = promptexpand(prompt3, 0, NULL, NULL);
zputs(str, stderr);
free(str);
fflush(stderr);
@@ -351,9 +351,16 @@ selectlist(LinkList l, size_t start)
zleentry(ZLE_CMD_TRASH);
arr = hlinklist2array(l, 0);
- for (ap = arr; *ap; ap++)
- if (strlen(*ap) > longest)
- longest = strlen(*ap);
+ for (ap = arr; *ap; ap++) {
+#ifdef MB_METASTRWIDTH
+ int aplen = MB_METASTRWIDTH(*ap);
+#else
+ int aplen = 0;
+ (void) unmetafy(*ap, &aplen);
+#endif
+ if (aplen > longest)
+ longest = aplen;
+ }
t0 = ct = ap - arr;
longest++;
while (t0)
@@ -368,7 +375,12 @@ selectlist(LinkList l, size_t start)
for (t1 = start; t1 != colsz && t1 - start < zterm_lines - 2; t1++) {
ap = arr + t1;
do {
+#ifdef MB_METASTRWIDTH
+ size_t t2 = MB_METASTRWIDTH(*ap) + 2;
+ (void) unmetafy(*ap, NULL);
+#else
size_t t2 = strlen(*ap) + 2;
+#endif
int t3;
fprintf(stderr, "%d) %s", t3 = ap - arr + 1, *ap);
@@ -428,7 +440,7 @@ execwhile(Estate state, UNUSED(int do_exec))
} else {
for (;;) {
state->pc = loop;
- noerrexit = NOERREXIT_EXIT | NOERREXIT_RETURN;
+ noerrexit |= NOERREXIT_EXIT | NOERREXIT_RETURN;
/* In case the test condition is a functional no-op,
* make sure signal handlers recognize ^C to end the loop. */
@@ -569,23 +581,14 @@ execif(Estate state, int do_exec)
s = 1;
state->pc = next;
}
+ noerrexit = olderrexit;
if (run) {
- /* we need to ignore lastval until we reach execcmd() */
- if (olderrexit || run == 2)
- noerrexit = olderrexit;
- else if (lastval)
- noerrexit |= NOERREXIT_EXIT | NOERREXIT_RETURN | NOERREXIT_UNTIL_EXEC;
- else
- noerrexit &= ~ (NOERREXIT_EXIT | NOERREXIT_RETURN);
cmdpush(run == 2 ? CS_ELSE : (s ? CS_ELIFTHEN : CS_IFTHEN));
execlist(state, 1, do_exec);
cmdpop();
- } else {
- noerrexit = olderrexit;
- if (!retflag && !errflag)
- lastval = 0;
- }
+ } else if (!retflag && !errflag)
+ lastval = 0;
state->pc = end;
this_noerrexit = 1;
@@ -793,6 +796,7 @@ exectry(Estate state, int do_exec)
cmdpop();
popheap();
state->pc = end;
+ this_noerrexit = 1;
return endval;
}
diff --git a/Src/makepro.awk b/Src/makepro.awk
index f69660531..56c4f4595 100644
--- a/Src/makepro.awk
+++ b/Src/makepro.awk
@@ -121,7 +121,7 @@ BEGIN {
# initialiser.
dcltor = substr(line, 1, RLENGTH-1)
line = substr(line, RLENGTH+1)
- sub(/=.*$/, "", dcltor)
+ sub(/\075.*$/, "", dcltor)
match(dcltor, /^([^_0-9A-Za-z]| const )*/)
dcltor = substr(dcltor, 1, RLENGTH) "@+" substr(dcltor, RLENGTH+1)
match(dcltor, /^.*@\+[_0-9A-Za-z]+/)
@@ -131,8 +131,8 @@ BEGIN {
sub(/@-.*$/, "", dnam)
# Put parens etc. back
- gsub(/@[{]/, " _((", dcltor)
- gsub(/@}/, "))", dcltor)
+ gsub(/@[{]/, " (", dcltor)
+ gsub(/@}/, ")", dcltor)
gsub(/@</, "(", dcltor)
gsub(/@>/, ")", dcltor)
gsub(/@!/, ",", dcltor)
diff --git a/Src/math.c b/Src/math.c
index 777ad9c31..d97dae238 100644
--- a/Src/math.c
+++ b/Src/math.c
@@ -352,6 +352,8 @@ getmathparam(struct mathvalue *mptr)
}
return zero_mnumber;
}
+ if (errflag)
+ return zero_mnumber;
}
result = getnumvalue(mptr->pval);
if (isset(FORCEFLOAT) && result.type == MN_INTEGER) {
@@ -641,7 +643,9 @@ zzlex(void)
return MINUSEQ;
}
if (unary) {
- if (idigit(*ptr) || *ptr == '.') {
+ if (idigit(*ptr) ||
+ (*ptr == '.' &&
+ (idigit(ptr[1]) || !itype_end(ptr, INAMESPC, 0)))) {
int ctype = lexconstant();
if (ctype == NUM)
{
@@ -835,7 +839,9 @@ zzlex(void)
case Dnull:
break;
default:
- if (idigit(*--ptr) || *ptr == '.')
+ if (idigit(*--ptr) ||
+ (*ptr == '.' &&
+ (idigit(ptr[1]) || !itype_end(ptr, INAMESPC, 0))))
return lexconstant();
if (*ptr == '#') {
if (*++ptr == '\\' || *ptr == '#') {
@@ -857,7 +863,7 @@ zzlex(void)
}
cct = 1;
}
- if ((ie = itype_end(ptr, IIDENT, 0)) != ptr) {
+ if ((ie = itype_end(ptr, INAMESPC, 0)) != ptr) {
int func = 0;
char *p;
@@ -955,7 +961,7 @@ getcvar(char *s)
}
}
#endif
- mn.u.l = STOUC(*t == Meta ? t[1] ^ 32 : *t);
+ mn.u.l = (unsigned char) (*t == Meta ? t[1] ^ 32 : *t);
}
unqueue_signals();
return mn;
@@ -1363,8 +1369,11 @@ op(int what)
}
spval = &stack[sp].val;
- if (stack[sp].val.type == MN_UNSET)
+ if (stack[sp].val.type == MN_UNSET) {
*spval = getmathparam(stack + sp);
+ if (errflag)
+ return;
+ }
switch (what) {
case NOT:
if (spval->type & MN_FLOAT) {
@@ -1548,21 +1557,32 @@ checkunary(int mtokc, char *mptr)
errmsg = 2;
}
if (errmsg) {
- int len, over = 0;
+ int len = 0, over = 0;
char *errtype = errmsg == 2 ? "operator" : "operand";
while (inblank(*mptr))
mptr++;
- len = ztrlen(mptr);
- if (len > 10) {
- len = 10;
- over = 1;
+ if (isset(MULTIBYTE))
+ MB_CHARINIT();
+ while (over < 10 && mptr[len]) {
+ if (isset(MULTIBYTE))
+ len += MB_METACHARLEN(mptr+len);
+ else
+ len += (mptr[len] == Meta ? 2 : 1);
+ ++over;
+ }
+ if ((over = mptr[len])) {
+ mptr = dupstring(mptr);
+ if (mptr[len] == Meta)
+ mptr[len+1] = 0;
+ else
+ mptr[len] = 0;
}
if (!*mptr)
zerr("bad math expression: %s expected at end of string",
errtype);
else
- zerr("bad math expression: %s expected at `%l%s'",
- errtype, mptr, len, over ? "..." : "");
+ zerr("bad math expression: %s expected at `%s%s'",
+ errtype, mptr, over ? "..." : "");
}
unary = !(tp & OP_OPF);
}
diff --git a/Src/mem.c b/Src/mem.c
index fb4be47bf..0b6f76e46 100644
--- a/Src/mem.c
+++ b/Src/mem.c
@@ -1057,17 +1057,17 @@ zrealloc(void *ptr, size_t size)
#if !defined(__hpux) && !defined(DGUX) && !defined(__osf__)
# if defined(_BSD)
# ifndef HAVE_BRK_PROTO
- extern int brk _((caddr_t));
+ extern int brk (caddr_t);
# endif
# ifndef HAVE_SBRK_PROTO
- extern caddr_t sbrk _((int));
+ extern caddr_t sbrk (int);
# endif
# else
# ifndef HAVE_BRK_PROTO
- extern int brk _((void *));
+ extern int brk (void *);
# endif
# ifndef HAVE_SBRK_PROTO
- extern void *sbrk _((int));
+ extern void *sbrk (int);
# endif
# endif
#endif
diff --git a/Src/mkbltnmlst.sh b/Src/mkbltnmlst.sh
index c4611d8b3..1994ace60 100644
--- a/Src/mkbltnmlst.sh
+++ b/Src/mkbltnmlst.sh
@@ -76,6 +76,30 @@ for x_mod in $x_mods; do
test "x$linked" = xno && echo "#endif"
done
+# if dynamic module 'mod' with load=no has moddeps in its .mdd,
+# then output add_dep(mod, dep) for each 'dep' in moddeps.
+dyn_mods="`grep ' link=dynamic .* load=no ' $CFMOD | \
+ sed -e '/^#/d' -e 's/ .*/ /' -e 's/^name=/ /'`"
+
+for mod in $dyn_mods; do
+ modfile="`grep '^name='$mod' ' $CFMOD | \
+ sed -e 's/^.* modfile=//' -e 's/ .*//'`"
+ if test "x$modfile" = x; then
+ echo >&2 "WARNING: no name for \`$mod' in $CFMOD (ignored)"
+ continue
+ fi
+ unset moddeps
+ . $srcdir/../$modfile
+ if test -n "$moddeps"; then
+ echo '#ifdef DYNAMIC'
+ echo "/* non-linked-in known module \`$mod' */"
+ for dep in $moddeps; do
+ echo " add_dep(\"$mod\", \"$dep\");"
+ done
+ echo '#endif'
+ fi
+done
+
echo
done_mods=" "
for bin_mod in $bin_mods; do
@@ -98,12 +122,12 @@ for bin_mod in $bin_mods; do
esac
done
echo " {"
- echo " extern int setup_${q_bin_mod} _((Module));"
- echo " extern int boot_${q_bin_mod} _((Module));"
- echo " extern int features_${q_bin_mod} _((Module,char***));"
- echo " extern int enables_${q_bin_mod} _((Module,int**));"
- echo " extern int cleanup_${q_bin_mod} _((Module));"
- echo " extern int finish_${q_bin_mod} _((Module));"
+ echo " extern int setup_${q_bin_mod} (Module);"
+ echo " extern int boot_${q_bin_mod} (Module);"
+ echo " extern int features_${q_bin_mod} (Module,char***);"
+ echo " extern int enables_${q_bin_mod} (Module,int**);"
+ echo " extern int cleanup_${q_bin_mod} (Module);"
+ echo " extern int finish_${q_bin_mod} (Module);"
echo
echo " register_module(\"$bin_mod\","
echo " setup_${q_bin_mod},"
diff --git a/Src/modentry.c b/Src/modentry.c
index 4d8217f43..23c499d94 100644
--- a/Src/modentry.c
+++ b/Src/modentry.c
@@ -1,10 +1,10 @@
#include "zsh.mdh"
-int setup_ _((Module));
-int boot_ _((Module));
-int cleanup_ _((Module));
-int finish_ _((Module));
-int modentry _((int boot, Module m, void *ptr));
+int setup_ (Module);
+int boot_ (Module);
+int cleanup_ (Module);
+int finish_ (Module);
+int modentry (int boot, Module m, void *ptr);
/**/
int
diff --git a/Src/module.c b/Src/module.c
index bab4d8d73..b4b5d0a2c 100644
--- a/Src/module.c
+++ b/Src/module.c
@@ -356,7 +356,7 @@ finish_(UNUSED(Module m))
/**/
void
-register_module(char *n, Module_void_func setup,
+register_module(const char *n, Module_void_func setup,
Module_features_func features,
Module_enables_func enables,
Module_void_func boot,
@@ -846,7 +846,7 @@ Hookdef hooktab;
/**/
Hookdef
-gethookdef(char *n)
+gethookdef(const char *n)
{
Hookdef p;
@@ -974,7 +974,7 @@ deletehookdeffunc(Hookdef h, Hookfn f)
/**/
mod_export int
-deletehookfunc(char *n, Hookfn f)
+deletehookfunc(const char *n, Hookfn f)
{
Hookdef h = gethookdef(n);
@@ -1198,6 +1198,7 @@ add_autoparam(const char *module, const char *pnam, int flags)
{
Param pm;
int ret;
+ int ne = noerrs;
queue_signals();
if ((ret = checkaddparam(pnam, (flags & FEAT_IGNORE)))) {
@@ -1212,14 +1213,18 @@ add_autoparam(const char *module, const char *pnam, int flags)
return ret == 2 ? 0 : -1;
}
- pm = setsparam(dupstring(pnam), ztrdup(module));
-
- pm->node.flags |= PM_AUTOLOAD;
- if (flags & FEAT_AUTOALL)
- pm->node.flags |= PM_AUTOALL;
+ noerrs = 2;
+ if ((pm = setsparam(dupstring(pnam), ztrdup(module)))) {
+ pm->node.flags |= PM_AUTOLOAD;
+ if (flags & FEAT_AUTOALL)
+ pm->node.flags |= PM_AUTOALL;
+ ret = 0;
+ } else
+ ret = -1;
+ noerrs = ne;
unqueue_signals();
- return 0;
+ return ret;
}
/* Remove a parameter added with add_autoparam() */
@@ -1761,7 +1766,7 @@ dyn_finish_module(Module m)
#else
static Module_generic_func
-module_func(Module m, char *name)
+module_func(Module m, const char *name)
{
#ifdef DYNAMIC_NAME_CLASH_OK
return (Module_generic_func) dlsym(m->u.handle, name);
@@ -2438,7 +2443,7 @@ bin_zmodload(char *nam, char **args, Options ops, UNUSED(int func))
int ops_au = OPT_ISSET(ops,'a') || OPT_ISSET(ops,'u');
int ret = 1, autoopts;
/* options only allowed with -F */
- char *fonly = "lP", *fp;
+ const char *fonly = "lP", *fp;
if (ops_bcpf && !ops_au) {
zwarnnam(nam, "-b, -c, -f, and -p must be combined with -a or -u");
@@ -2474,7 +2479,7 @@ bin_zmodload(char *nam, char **args, Options ops, UNUSED(int func))
return 1;
}
for (fp = fonly; *fp; fp++) {
- if (OPT_ISSET(ops,STOUC(*fp)) && !OPT_ISSET(ops,'F')) {
+ if (OPT_ISSET(ops,(unsigned char) *fp) && !OPT_ISSET(ops,'F')) {
zwarnnam(nam, "-%c is only allowed with -F", *fp);
return 1;
}
@@ -3177,7 +3182,7 @@ bin_zmodload_features(const char *nam, char **args, Options ops)
} else if (OPT_ISSET(ops, 'L'))
printf("zmodload -F %s ", m->node.nam);
for (fp = features, ep = enables; *fp; fp++, ep++) {
- char *onoff;
+ const char *onoff;
int term;
if (*args) {
char **argp;
@@ -3447,7 +3452,8 @@ autofeatures(const char *cmdnam, const char *module, char **features,
defm = NULL;
for (; *features; features++) {
- char *fnam, *typnam, *feature;
+ char *fnam, *feature;
+ const char *typnam;
int add, fchar, flags = defflags;
autofeaturefn_t fn;
diff --git a/Src/options.c b/Src/options.c
index a1fe918fc..8b13f0c5d 100644
--- a/Src/options.c
+++ b/Src/options.c
@@ -877,34 +877,37 @@ dosetopt(int optno, int value, int force, char *new_opts)
}
#endif /* HAVE_SETRESGID && HAVE_SETRESUID */
-#ifdef JOB_CONTROL
} else if (!force && optno == MONITOR && value) {
if (new_opts[optno] == value)
return 0;
- if (SHTTY != -1) {
+ if (SHTTY == -1)
+ return -1;
+ if (!origpgrp) {
origpgrp = GETPGRP();
acquire_pgrp();
- } else
- return -1;
-#else
- } else if(optno == MONITOR && value) {
- return -1;
-#endif /* not JOB_CONTROL */
-#ifdef GETPWNAM_FAKED
- } else if(optno == CDABLEVARS && value) {
- return -1;
-#endif /* GETPWNAM_FAKED */
+ }
} else if ((optno == EMACSMODE || optno == VIMODE) && value) {
+ /* What's going on here:
+ * 1) unsetopt of either emacs or vi is an effective no-op.
+ * 2) setopt of either emacs or vi toggles off the other.
+ * Hence we disallow changing these options in emulation mode,
+ * but only if the change is setopt rather than unsetopt.
+ */
if (sticky && sticky->emulation)
- return -1;
- zleentry(ZLE_CMD_SET_KEYMAP, optno);
- new_opts[(optno == EMACSMODE) ? VIMODE : EMACSMODE] = 0;
+ return opts[optno] ? 0 : -1;
+ if (zle_load_state == 1)
+ zleentry(ZLE_CMD_SET_KEYMAP, optno);
+ new_opts[optno ^ EMACSMODE ^ VIMODE] = 0;
} else if (optno == SUNKEYBOARDHACK) {
/* for backward compatibility */
keyboardhackchar = (value ? '`' : '\0');
}
new_opts[optno] = value;
- if (optno == BANGHIST || optno == SHINSTDIN)
+ if (
+#ifdef MULTIBYTE_SUPPORT
+ optno == MULTIBYTE ||
+#endif
+ optno == BANGHIST || optno == SHINSTDIN)
inittyptab();
return 0;
}
diff --git a/Src/params.c b/Src/params.c
index 27ea82298..c10236a0d 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -137,11 +137,11 @@ unsigned char hatchar, hashchar;
unsigned char keyboardhackchar = '\0';
/* $SECONDS = now.tv_sec - shtimer.tv_sec
- * + (now.tv_usec - shtimer.tv_usec) / 1000000.0
+ * + (now.tv_nsec - shtimer.tv_nsec) / 1000000000.0
* (rounded to an integer if the parameter is not set to float) */
/**/
-struct timeval shtimer;
+struct timespec shtimer;
/* 0 if this $TERM setup is usable, otherwise it contains TERM_* flags */
@@ -475,6 +475,16 @@ static initparam argvparam_pm = IPDEF9("", &pparams, NULL, \
((V) && (!(V)->pm || ((V)->pm->node.flags & PM_UNSET) || \
!(V)->pm->node.nam || !*(V)->pm->node.nam))
+/*
+ * For named references. Simple named references are just like scalars
+ * for efficiency, but special named references need get/set functions.
+ */
+#define GETREFNAME(PM) (((PM)->node.flags & PM_SPECIAL) ? \
+ (PM)->gsu.s->getfn(PM) : (PM)->u.str)
+#define SETREFNAME(PM,S) (((PM)->node.flags & PM_SPECIAL) ? \
+ (PM)->gsu.s->setfn(PM,(S)) : \
+ (zsfree((PM)->u.str), (PM)->u.str = (S)))
+
static Param argvparam;
/* "parameter table" - hash table containing the parameters
@@ -520,7 +530,7 @@ getparamnode(HashTable ht, const char *nam)
HashNode hn = gethashnode2(ht, nam);
Param pm = (Param) hn;
- if (pm && pm->u.str && (pm->node.flags & PM_AUTOLOAD)) {
+ if (pm && (pm->node.flags & PM_AUTOLOAD) && pm->u.str) {
char *mn = dupstring(pm->u.str);
(void)ensurefeature(mn, "p:", (pm->node.flags & PM_AUTOALL) ? NULL :
@@ -536,6 +546,9 @@ getparamnode(HashTable ht, const char *nam)
nam);
}
}
+
+ if (hn && ht == realparamtab && !(hn->flags & PM_UNSET))
+ hn = resolve_nameref((Param)hn, NULL);
return hn;
}
@@ -732,7 +745,7 @@ split_env_string(char *env, char **name, char **value)
tenv = strcpy(zhalloc(strlen(env) + 1), env);
for (str = tenv; *str && *str != '='; str++) {
- if (STOUC(*str) >= 128) {
+ if ((unsigned char) *str >= 128) {
/*
* We'll ignore environment variables with names not
* from the portable character set since we don't
@@ -838,12 +851,11 @@ createparamtable(void)
setsparam("HOST", ztrdup_metafy(hostnam));
zfree(hostnam, 256);
- setsparam("LOGNAME", ztrdup_metafy(
+ setsparam("LOGNAME",
#ifndef DISABLE_DYNAMIC_NSS
- (str = getlogin()) && *str ? str :
+ (str = getlogin()) && *str ? ztrdup_metafy(str) :
#endif
- cached_username
- ));
+ ztrdup(cached_username));
#if !defined(HAVE_PUTENV) && !defined(USE_SET_UNSET_ENV)
/* Copy the environment variables we are inheriting to dynamic *
@@ -935,8 +947,18 @@ createparamtable(void)
setsparam("ZSH_ARGZERO", ztrdup(posixzero));
setsparam("ZSH_VERSION", ztrdup_metafy(ZSH_VERSION));
setsparam("ZSH_PATCHLEVEL", ztrdup_metafy(ZSH_PATCHLEVEL));
- setaparam("signals", sigptr = zalloc((SIGCOUNT+4) * sizeof(char *)));
- for (t = sigs; (*sigptr++ = ztrdup_metafy(*t++)); );
+ setaparam("signals", sigptr = zalloc((TRAPCOUNT + 1) * sizeof(char *)));
+ t = sigs;
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+ while (t - sigs <= SIGCOUNT)
+ *sigptr++ = ztrdup_metafy(*t++);
+ {
+ int sig;
+ for (sig = SIGRTMIN; sig <= SIGRTMAX; sig++)
+ *sigptr++ = ztrdup_metafy(rtsigname(sig, 0));
+ }
+#endif
+ while ((*sigptr++ = ztrdup_metafy(*t++))) /* empty */ ;
noerrs = 0;
}
@@ -993,6 +1015,62 @@ createparam(char *name, int flags)
gethashnode2(paramtab, name) :
paramtab->getnode(paramtab, name));
+ if (oldpm && (oldpm->node.flags & PM_RO_BY_DESIGN)) {
+ if (!(flags & PM_LOCAL)) {
+ /* Must call the API for namerefs and specials to work */
+ pm = (Param) paramtab->getnode2(paramtab, oldpm->node.nam);
+ if (!pm || ((pm->node.flags & PM_NAMEREF) &&
+ pm->level != locallevel)) {
+ zerr("%s: can't modify read-only parameter", name);
+ return NULL;
+ }
+ }
+ /**
+ * Implementation note: In the case of a readonly nameref,
+ * the right thing might be to insert a new global into
+ * the paramtab and point the local pm->old at it, rather
+ * than error. That is why gethashnode2() is called
+ * first, to avoid skipping up the stack prematurely.
+ **/
+ }
+
+ if (oldpm && !(flags & PM_NAMEREF) &&
+ (oldpm->level == locallevel ?
+ !(oldpm->node.flags & PM_RO_BY_DESIGN) : !(flags & PM_LOCAL)) &&
+ (oldpm->node.flags & PM_NAMEREF) &&
+ (oldpm = upscope(oldpm, oldpm->base))) {
+ Param lastpm;
+ struct asgment stop;
+ stop.flags = PM_NAMEREF | (flags & PM_LOCAL);
+ stop.name = oldpm->node.nam;
+ stop.value.scalar = GETREFNAME(oldpm);
+ lastpm = (Param)resolve_nameref(oldpm, &stop);
+ if (lastpm) {
+ if (lastpm->node.flags & PM_NAMEREF) {
+ char *refname = GETREFNAME(lastpm);
+ if (refname && *refname) {
+ name = refname;
+ oldpm = NULL;
+ } else {
+ if (!(lastpm->node.flags & PM_READONLY)) {
+ if (flags) {
+ /* Only plain scalar assignment allowed */
+ zerr("%s: can't change type of named reference",
+ name); /* Differs from ksh93u+ */
+ return NULL;
+ }
+ }
+ return lastpm;
+ }
+ } else {
+ /* nameref pointing to an unset local */
+ DPUTS(!(lastpm->node.flags & PM_UNSET),
+ "BUG: local parameter is not unset");
+ oldpm = lastpm;
+ }
+ }
+ }
+
DPUTS(oldpm && oldpm->level > locallevel,
"BUG: old local parameter not deleted");
if (oldpm && (oldpm->level == locallevel || !(flags & PM_LOCAL))) {
@@ -1009,7 +1087,7 @@ createparam(char *name, int flags)
/* POSIXBUILTINS horror: we need to retain 'export' flags */
(isset(POSIXBUILTINS) && (oldpm->node.flags & PM_EXPORTED))) {
if (oldpm->node.flags & PM_RO_BY_DESIGN) {
- zerr("%s: can't change parameter attribute",
+ zerr("%s: can't modify read-only parameter",
name);
return NULL;
}
@@ -1185,6 +1263,26 @@ isident(char *s)
if (!*s) /* empty string is definitely not valid */
return 0;
+ /* This partly duplicates code in itype_end(), but we need to
+ * distinguish the leading namespace at this point to check the
+ * correctness of the identifier that follows
+ */
+ if (*s == '.') {
+ if (idigit(s[1]))
+ return 0; /* Namespace must not start with a digit */
+ /* Reject identifiers beginning with a digit in namespaces.
+ * Move this out below this block to also reject v.1x form.
+ */
+ if ((ss = itype_end(s + (*s == '.'), IIDENT, 0))) {
+ if (*ss == '.') {
+ if (!ss[1])
+ return 0;
+ if (idigit(ss[1]))
+ s = ss + 1;
+ }
+ }
+ }
+
if (idigit(*s)) {
/* If the first character is `s' is a digit, then all must be */
for (ss = ++s; *ss; ss++)
@@ -1192,7 +1290,7 @@ isident(char *s)
break;
} else {
/* Find the first character in `s' not in the iident type table */
- ss = itype_end(s, IIDENT, 0);
+ ss = itype_end(s, INAMESPC, 0);
}
/* If the next character is not [, then it is *
@@ -1262,7 +1360,6 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w,
/* first parse any subscription flags */
if (v->pm && (*s == '(' || *s == Inpar)) {
int escapes = 0;
- int waste;
for (s++; *s != ')' && *s != Outpar && s != *str; s++) {
switch (*s) {
case 'r':
@@ -1339,8 +1436,13 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w,
sav = *t;
*t = '\0';
s += arglen;
- sep = escapes ? getkeystring(s, &waste, GETKEYS_SEP, NULL)
- : dupstring(s);
+ if (escapes) {
+ int len;
+ sep = getkeystring(s, &len, GETKEYS_SEP, NULL);
+ sep = metafy(sep, len, META_HREALLOC);
+ }
+ else
+ sep = dupstring(s);
*t = sav;
s = t + arglen - 1;
break;
@@ -1432,10 +1534,14 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w,
if (ishash && (keymatch || !rev))
remnulargs(s);
if (needtok) {
+ char exe = opts[EXECOPT];
s = dupstring(s);
if (parsestr(&s))
return 0;
+ if (flags & SCANPM_NOEXEC)
+ opts[EXECOPT] = 0;
singsub(&s);
+ opts[EXECOPT] = exe;
} else if (rev)
remnulargs(s); /* This is probably always a no-op, but ... */
if (!rev) {
@@ -1665,7 +1771,7 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w,
/* Searching characters */
int slen;
d = getstrvalue(v);
- if (!d || !*d)
+ if (!d)
return 0;
/*
* beg and len are character counts, not raw offsets.
@@ -2047,6 +2153,7 @@ fetchvalue(Value v, char **pptr, int bracks, int flags)
char *s, *t, *ie;
char sav, c;
int ppar = 0;
+ int itype = (flags & SCANPM_NONAMESPC) ? IIDENT : INAMESPC;
s = t = *pptr;
@@ -2056,7 +2163,7 @@ fetchvalue(Value v, char **pptr, int bracks, int flags)
else
ppar = *s++ - '0';
}
- else if ((ie = itype_end(s, IIDENT, 0)) != s)
+ else if ((ie = itype_end(s, itype, 0)) != s)
s = ie;
else if (c == Quest)
*s++ = '?';
@@ -2094,8 +2201,16 @@ fetchvalue(Value v, char **pptr, int bracks, int flags)
int isvarat;
isvarat = (t[0] == '@' && !t[1]);
- pm = (Param) paramtab->getnode(paramtab, *t == '0' ? "0" : t);
- if (sav)
+ if (flags & SCANPM_NONAMEREF)
+ pm = (Param) paramtab->getnode2(paramtab, *t == '0' ? "0" : t);
+ else
+ pm = (Param) paramtab->getnode(paramtab, *t == '0' ? "0" : t);
+ if (!pm && *t == '.' && !isident(t)) {
+ /* badly formed namespace reference */
+ if (sav)
+ *s = sav;
+ return NULL;
+ } else if (sav)
*s = sav;
*pptr = s;
if (!pm || ((pm->node.flags & PM_UNSET) &&
@@ -2105,6 +2220,29 @@ fetchvalue(Value v, char **pptr, int bracks, int flags)
memset(v, 0, sizeof(*v));
else
v = (Value) hcalloc(sizeof *v);
+ if ((pm->node.flags & PM_NAMEREF) && !(flags & SCANPM_NONAMEREF)) {
+ char *refname = GETREFNAME(pm);
+ if (refname && *refname) {
+ /* only happens for namerefs pointing to array elements */
+ char *ref = dupstring(refname);
+ char *ss = pm->width ? ref + pm->width : NULL;
+ if (ss) {
+ sav = *ss;
+ *ss = 0;
+ }
+ Param p1 = (Param)gethashnode2(paramtab, ref);
+ if (!(p1 && (pm = upscope(p1, pm->base))) ||
+ ((pm->node.flags & PM_UNSET) &&
+ !(pm->node.flags & PM_DECLARED)))
+ return NULL;
+ if (ss) {
+ flags |= SCANPM_NOEXEC;
+ *ss = sav;
+ s = dyncat(ss,*pptr);
+ } else
+ s = *pptr;
+ }
+ }
if (PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED)) {
/* Overload v->isarr as the flag bits for hashed arrays. */
v->isarr = flags | (isvarat ? SCANPM_ISVAR_AT : 0);
@@ -2124,7 +2262,7 @@ fetchvalue(Value v, char **pptr, int bracks, int flags)
return v;
}
} else if (!(flags & SCANPM_ASSIGNING) && v->isarr &&
- itype_end(t, IIDENT, 1) != t && isset(KSHARRAYS))
+ itype_end(t, INAMESPC, 1) != t && isset(KSHARRAYS))
v->end = 1, v->isarr = 0;
}
if (!bracks && *s)
@@ -2673,9 +2811,11 @@ assignstrvalue(Value v, char *val, int flags)
}
break;
}
- if ((!v->pm->env && !(v->pm->node.flags & PM_EXPORTED) &&
- !(isset(ALLEXPORT) && !(v->pm->node.flags & PM_HASHELEM))) ||
- (v->pm->node.flags & PM_ARRAY) || v->pm->ename)
+ setscope(v->pm);
+ if (errflag ||
+ ((!v->pm->env && !(v->pm->node.flags & PM_EXPORTED) &&
+ !(isset(ALLEXPORT) && !(v->pm->node.flags & PM_HASHELEM))) ||
+ (v->pm->node.flags & PM_ARRAY) || v->pm->ename))
return;
export_param(v->pm);
}
@@ -2924,8 +3064,9 @@ getsparam(char *s)
mod_export char *
getsparam_u(char *s)
{
+ /* getsparam() returns pointer into global params table, so ... */
if ((s = getsparam(s)))
- return unmetafy(s, NULL);
+ return unmeta(s); /* returns static pointer to copy */
return s;
}
@@ -3008,7 +3149,7 @@ check_warn_pm(Param pm, const char *pmtype, int created,
} else
return;
- if (pm->node.flags & PM_SPECIAL)
+ if (pm->node.flags & (PM_SPECIAL|PM_NAMEREF))
return;
for (i = funcstack; i; i = i->prev) {
@@ -3080,11 +3221,20 @@ assignsparam(char *s, char *val, int flags)
}
}
if (!v && !(v = getvalue(&vbuf, &t, 1))) {
- unqueue_signals();
zsfree(val);
+ unqueue_signals();
/* errflag |= ERRFLAG_ERROR; */
return NULL;
}
+ if (*val && (v->pm->node.flags & PM_NAMEREF)) {
+ if (!valid_refname(val)) {
+ zerr("invalid variable name: %s", val);
+ zsfree(val);
+ unqueue_signals();
+ errflag |= ERRFLAG_ERROR;
+ return NULL;
+ }
+ }
if (flags & ASSPM_WARN)
check_warn_pm(v->pm, "scalar", created, 1);
v->pm->node.flags &= ~PM_DEFAULTED;
@@ -3111,8 +3261,8 @@ assignsparam(char *s, char *val, int flags)
lhs.u.l = lhs.u.l + (zlong)rhs.u.d;
}
setnumvalue(v, lhs);
- unqueue_signals();
zsfree(val);
+ unqueue_signals();
return v->pm; /* avoid later setstrvalue() call */
case PM_ARRAY:
if (unset(KSHARRAYS)) {
@@ -3137,9 +3287,9 @@ assignsparam(char *s, char *val, int flags)
case PM_INTEGER:
case PM_EFLOAT:
case PM_FFLOAT:
+ zsfree(val);
unqueue_signals();
zerr("attempt to add to slice of a numeric variable");
- zsfree(val);
return NULL;
case PM_ARRAY:
kshappend:
@@ -3214,7 +3364,7 @@ assignaparam(char *s, char **val, int flags)
} else if (!(PM_TYPE(v->pm->node.flags) & (PM_ARRAY|PM_HASHED)) &&
!(v->pm->node.flags & (PM_SPECIAL|PM_TIED))) {
int uniq = v->pm->node.flags & PM_UNIQUE;
- if (flags & ASSPM_AUGMENT) {
+ if ((flags & ASSPM_AUGMENT) && !(v->pm->node.flags & PM_UNSET)) {
/* insert old value at the beginning of the val array */
char **new;
int lv = arrlen(val);
@@ -3505,9 +3655,18 @@ assignnparam(char *s, mnumber val, int flags)
pm = createparam(t, ss ? PM_ARRAY :
isset(POSIXIDENTIFIERS) ? PM_SCALAR :
(val.type & MN_INTEGER) ? PM_INTEGER : PM_FFLOAT);
- if (!pm)
- pm = (Param) paramtab->getnode(paramtab, t);
- DPUTS(!pm, "BUG: parameter not created");
+ if (errflag) {
+ /* assume error message already output */
+ unqueue_signals();
+ return NULL;
+ }
+ if (!pm && !(pm = (Param) paramtab->getnode(paramtab, t))) {
+ DPUTS(!pm, "BUG: parameter not created");
+ if (!errflag)
+ zerr("%s: parameter not found", t);
+ unqueue_signals();
+ return NULL;
+ }
if (ss) {
*ss = '[';
} else if (val.type & MN_INTEGER) {
@@ -3578,7 +3737,7 @@ mod_export Param
setiparam_no_convert(char *s, zlong val)
{
/*
- * If the target is already an integer, thisgets converted
+ * If the target is already an integer, this gets converted
* back. Low technology rules.
*/
char buf[BDIGBUFSIZE];
@@ -3598,7 +3757,8 @@ unsetparam(char *s)
if ((pm = (Param) (paramtab == realparamtab ?
/* getnode2() to avoid autoloading */
paramtab->getnode2(paramtab, s) :
- paramtab->getnode(paramtab, s))))
+ paramtab->getnode(paramtab, s))) &&
+ !(pm->node.flags & PM_NAMEREF))
unsetparam_pm(pm, 0, 1);
unqueue_signals();
}
@@ -3617,7 +3777,9 @@ unsetparam_pm(Param pm, int altflag, int exp)
char *altremove;
if ((pm->node.flags & PM_READONLY) && pm->level <= locallevel) {
- zerr("read-only variable: %s", pm->node.nam);
+ zerr("read-only %s: %s",
+ (pm->node.flags & PM_NAMEREF) ? "reference" : "variable",
+ pm->node.nam);
return 1;
}
if ((pm->node.flags & PM_RESTRICTED) && isset(RESTRICTED)) {
@@ -3631,7 +3793,7 @@ unsetparam_pm(Param pm, int altflag, int exp)
altremove = NULL;
pm->node.flags &= ~PM_DECLARED; /* like ksh, not like bash */
- if (!(pm->node.flags & PM_UNSET))
+ if (!(pm->node.flags & PM_UNSET) || (pm->node.flags & PM_REMOVABLE))
pm->gsu.s->unsetfn(pm, exp);
if (pm->env)
delenv(pm);
@@ -3660,12 +3822,15 @@ unsetparam_pm(Param pm, int altflag, int exp)
/* fudge things so removenode isn't called */
altpm->level = 1;
}
- unsetparam_pm(altpm, 1, exp);
+ unsetparam_pm(altpm, 1, exp); /* This resets pm to empty */
+ pm->node.flags |= PM_UNSET; /* so we must repeat this */
}
zsfree(altremove);
- if (!(pm->node.flags & PM_SPECIAL))
+ if (!(pm->node.flags & PM_SPECIAL)) {
pm->gsu.s = &stdscalar_gsu;
+ pm->node.flags &= ~PM_ARRAY;
+ }
}
/*
@@ -4119,7 +4284,8 @@ char *
tiedarrgetfn(Param pm)
{
struct tieddata *dptr = (struct tieddata *)pm->u.data;
- return *dptr->arrptr ? zjoin(*dptr->arrptr, STOUC(dptr->joinchar), 1) : "";
+ return *dptr->arrptr ?
+ zjoin(*dptr->arrptr, (unsigned char) dptr->joinchar, 1) : "";
}
/**/
@@ -4330,13 +4496,12 @@ randomsetfn(UNUSED(Param pm), zlong v)
zlong
intsecondsgetfn(UNUSED(Param pm))
{
- struct timeval now;
- struct timezone dummy_tz;
+ struct timespec now;
- gettimeofday(&now, &dummy_tz);
+ zgettime_monotonic_if_available(&now);
return (zlong)(now.tv_sec - shtimer.tv_sec -
- (now.tv_usec < shtimer.tv_usec ? 1 : 0));
+ (now.tv_nsec < shtimer.tv_nsec ? 1 : 0));
}
/* Function to set value of special parameter `SECONDS' */
@@ -4345,48 +4510,47 @@ intsecondsgetfn(UNUSED(Param pm))
void
intsecondssetfn(UNUSED(Param pm), zlong x)
{
- struct timeval now;
- struct timezone dummy_tz;
+ struct timespec now;
zlong diff;
- gettimeofday(&now, &dummy_tz);
+ zgettime_monotonic_if_available(&now);
+
diff = (zlong)now.tv_sec - x;
shtimer.tv_sec = diff;
if ((zlong)shtimer.tv_sec != diff)
zwarn("SECONDS truncated on assignment");
- shtimer.tv_usec = now.tv_usec;
+ shtimer.tv_nsec = now.tv_nsec;
}
/**/
double
floatsecondsgetfn(UNUSED(Param pm))
{
- struct timeval now;
- struct timezone dummy_tz;
+ struct timespec now;
- gettimeofday(&now, &dummy_tz);
+ zgettime_monotonic_if_available(&now);
return (double)(now.tv_sec - shtimer.tv_sec) +
- (double)(now.tv_usec - shtimer.tv_usec) / 1000000.0;
+ (double)(now.tv_nsec - shtimer.tv_nsec) / 1000000000.0;
}
/**/
void
floatsecondssetfn(UNUSED(Param pm), double x)
{
- struct timeval now;
- struct timezone dummy_tz;
+ struct timespec now;
+
+ zgettime_monotonic_if_available(&now);
- gettimeofday(&now, &dummy_tz);
shtimer.tv_sec = now.tv_sec - (zlong)x;
- shtimer.tv_usec = now.tv_usec - (zlong)((x - (zlong)x) * 1000000.0);
+ shtimer.tv_nsec = now.tv_nsec - (zlong)((x - (zlong)x) * 1000000000.0);
}
/**/
double
getrawseconds(void)
{
- return (double)shtimer.tv_sec + (double)shtimer.tv_usec / 1000000.0;
+ return (double)shtimer.tv_sec + (double)shtimer.tv_nsec / 1000000000.0;
}
/**/
@@ -4394,7 +4558,7 @@ void
setrawseconds(double x)
{
shtimer.tv_sec = (zlong)x;
- shtimer.tv_usec = (zlong)((x - (zlong)x) * 1000000.0);
+ shtimer.tv_nsec = (zlong)((x - (zlong)x) * 1000000000.0);
}
/**/
@@ -4446,7 +4610,7 @@ usernamesetfn(UNUSED(Param pm), char *x)
zwarn("failed to change user ID: %e", errno);
else {
zsfree(cached_username);
- cached_username = ztrdup(pswd->pw_name);
+ cached_username = ztrdup_metafy(pswd->pw_name);
cached_uid = pswd->pw_uid;
}
}
@@ -4635,6 +4799,7 @@ setlang(char *x)
if ((x = getsparam_u(ln->name)) && *x)
setlocale(ln->category, x);
unqueue_signals();
+ inittyptab();
}
/**/
@@ -4658,6 +4823,7 @@ lc_allsetfn(Param pm, char *x)
else {
setlocale(LC_ALL, unmeta(x));
clear_mbstate();
+ inittyptab();
}
}
@@ -4696,6 +4862,7 @@ lcsetfn(Param pm, char *x)
}
unqueue_signals();
clear_mbstate(); /* LC_CTYPE may have changed */
+ inittyptab();
}
#endif /* USE_LOCALE */
@@ -4815,12 +4982,12 @@ keyboardhacksetfn(UNUSED(Param pm), char *x)
zwarn("Only one KEYBOARD_HACK character can be defined"); /* could be changed if needed */
}
for (i = 0; i < len; i++) {
- if (!isascii(STOUC(x[i]))) {
+ if (!isascii((unsigned char) x[i])) {
zwarn("KEYBOARD_HACK can only contain ASCII characters");
return;
}
}
- keyboardhackchar = len ? STOUC(x[0]) : '\0';
+ keyboardhackchar = len ? (unsigned char) x[0] : '\0';
free(x);
} else
keyboardhackchar = '\0';
@@ -4854,14 +5021,14 @@ histcharssetfn(UNUSED(Param pm), char *x)
if (len > 3)
len = 3;
for (i = 0; i < len; i++) {
- if (!isascii(STOUC(x[i]))) {
+ if (!isascii((unsigned char) x[i])) {
zwarn("HISTCHARS can only contain ASCII characters");
return;
}
}
- bangchar = len ? STOUC(x[0]) : '\0';
- hatchar = len > 1 ? STOUC(x[1]) : '\0';
- hashchar = len > 2 ? STOUC(x[2]) : '\0';
+ bangchar = len ? (unsigned char) x[0] : '\0';
+ hatchar = len > 1 ? (unsigned char) x[1] : '\0';
+ hashchar = len > 2 ? (unsigned char) x[2] : '\0';
free(x);
} else {
bangchar = '!';
@@ -5083,7 +5250,7 @@ arrfixenv(char *s, char **t)
if (pm->node.flags & PM_SPECIAL)
joinchar = ':';
else
- joinchar = STOUC(((struct tieddata *)pm->u.data)->joinchar);
+ joinchar = (unsigned char) ((struct tieddata *)pm->u.data)->joinchar;
addenv(pm, t ? zjoin(t, joinchar, 1) : "");
}
@@ -5105,9 +5272,9 @@ zputenv(char *str)
char *ptr;
int ret;
- for (ptr = str; *ptr && STOUC(*ptr) < 128 && *ptr != '='; ptr++)
+ for (ptr = str; *ptr && (unsigned char) *ptr < 128 && *ptr != '='; ptr++)
;
- if (STOUC(*ptr) >= 128) {
+ if ((unsigned char) *ptr >= 128) {
/*
* Environment variables not in the portable character
* set are non-standard and we don't really know of
@@ -5718,7 +5885,8 @@ scanendscope(HashNode hn, UNUSED(int flags))
export_param(pm);
} else
unsetparam_pm(pm, 0, 0);
- }
+ } else if ((pm->node.flags & PM_NAMEREF) && pm->base > pm->level)
+ pm->base = locallevel;
}
@@ -5769,6 +5937,7 @@ static const struct paramtypes pmtypes[] = {
{ PM_ARRAY, "array", 'a', 0},
{ PM_HASHED, "association", 'A', 0},
{ 0, "local", 0, PMTF_TEST_LEVEL},
+ { PM_HIDE, "hide", 'h', 0 },
{ PM_LEFT, "left justified", 'L', PMTF_USE_WIDTH},
{ PM_RIGHT_B, "right justified", 'R', PMTF_USE_WIDTH},
{ PM_RIGHT_Z, "zero filled", 'Z', PMTF_USE_WIDTH},
@@ -5778,7 +5947,8 @@ static const struct paramtypes pmtypes[] = {
{ PM_TAGGED, "tagged", 't', 0},
{ PM_EXPORTED, "exported", 'x', 0},
{ PM_UNIQUE, "unique", 'U', 0},
- { PM_TIED, "tied", 'T', 0}
+ { PM_TIED, "tied", 'T', 0},
+ { PM_NAMEREF, "nameref", 'n', 0}
};
#define PMTYPES_SIZE ((int)(sizeof(pmtypes)/sizeof(struct paramtypes)))
@@ -5875,7 +6045,12 @@ printparamnode(HashNode hn, int printflags)
{
Param p = (Param) hn;
Param peer = NULL;
+ int altname = 0;
+ if (!(p->node.flags & PM_HASHELEM) &&
+ !(printflags & PRINT_WITH_NAMESPACE) && *(p->node.nam) == '.')
+ return;
+
if (p->node.flags & PM_UNSET) {
if ((printflags & (PRINT_POSIX_READONLY|PRINT_POSIX_EXPORT) &&
p->node.flags & (PM_READONLY|PM_EXPORTED)) ||
@@ -5893,13 +6068,21 @@ printparamnode(HashNode hn, int printflags)
printflags |= PRINT_NAMEONLY;
if (printflags & (PRINT_TYPESET|PRINT_POSIX_READONLY|PRINT_POSIX_EXPORT)) {
- if (p->node.flags & (PM_RO_BY_DESIGN|PM_AUTOLOAD)) {
+ if (p->node.flags & PM_AUTOLOAD) {
/*
* It's not possible to restore the state of
* these, so don't output.
*/
return;
}
+ if (p->node.flags & PM_RO_BY_DESIGN) {
+ /*
+ * Compromise: cannot be restored out of context,
+ * but show anyway if printed in scope of declaration
+ */
+ if (p->level != locallevel || p->level == 0)
+ return;
+ }
/*
* The zsh variants of export -p/readonly -p also report other
* flags to indicate other attributes or scope. The POSIX variants
@@ -5908,16 +6091,26 @@ printparamnode(HashNode hn, int printflags)
if (printflags & PRINT_POSIX_EXPORT) {
if (!(p->node.flags & PM_EXPORTED))
return;
+ altname = 'x';
printf("export ");
} else if (printflags & PRINT_POSIX_READONLY) {
if (!(p->node.flags & PM_READONLY))
return;
+ altname = 'r';
printf("readonly ");
- } else if (locallevel && p->level >= locallevel) {
- printf("typeset "); /* printf("local "); */
} else if ((p->node.flags & PM_EXPORTED) &&
!(p->node.flags & (PM_ARRAY|PM_HASHED))) {
- printf("export ");
+ if (p->level && p->level >= locallevel)
+ printf("local ");
+ else {
+ altname = 'x';
+ printf("export ");
+ }
+ } else if (locallevel && p->level >= locallevel) {
+ if (p->node.flags & PM_EXPORTED)
+ printf("local ");
+ else
+ printf("typeset "); /* printf("local "); */
} else if (locallevel) {
printf("typeset -g ");
} else
@@ -5931,9 +6124,24 @@ printparamnode(HashNode hn, int printflags)
for (pmptr = pmtypes, i = 0; i < PMTYPES_SIZE; i++, pmptr++) {
int doprint = 0;
+
+ if (altname && altname == pmptr->typeflag)
+ continue;
+
if (pmptr->flags & PMTF_TEST_LEVEL) {
- if (p->level)
+ if (p->level) {
+ /*
+ if ((p->node.flags & PM_SPECIAL) &&
+ (p->node.flags & PM_LOCAL) &&
+ !(p->node.flags & PM_HIDE)) {
+ if (doneminus)
+ putchar(' ');
+ printf("+h ");
+ doneminus = 0;
+ }
+ */
doprint = 1;
+ }
} else if ((pmptr->binflag != PM_EXPORTED || p->level ||
(p->node.flags & (PM_LOCAL|PM_ARRAY|PM_HASHED))) &&
(p->node.flags & pmptr->binflag))
@@ -6018,7 +6226,7 @@ printparamnode(HashNode hn, int printflags)
* append the join char for tied parameters if different from colon
* for typeset -p output.
*/
- unsigned char joinchar = STOUC(((struct tieddata *)peer->u.data)->joinchar);
+ unsigned char joinchar = (unsigned char) ((struct tieddata *)peer->u.data)->joinchar;
if (joinchar != ':') {
char buf[2];
buf[0] = joinchar;
@@ -6032,3 +6240,225 @@ printparamnode(HashNode hn, int printflags)
else if (!(printflags & PRINT_KV_PAIR))
putchar('\n');
}
+
+/**/
+mod_export HashNode
+resolve_nameref(Param pm, const Asgment stop)
+{
+ HashNode hn = (HashNode)pm;
+ const char *seek = stop ? stop->value.scalar : NULL;
+
+ if (pm && (pm->node.flags & PM_NAMEREF)) {
+ char *refname = GETREFNAME(pm);
+ if (pm->node.flags & (PM_UNSET|PM_TAGGED)) {
+ /* Semaphore with createparam() */
+ pm->node.flags &= ~PM_UNSET;
+ if (pm->node.flags & PM_NEWREF) /* See setloopvar() */
+ return NULL;
+ if (refname && *refname && (pm->node.flags & PM_TAGGED))
+ pm->node.flags |= PM_SELFREF; /* See setscope() */
+ return (HashNode) pm;
+ } else if (refname) {
+ if ((pm->node.flags & PM_TAGGED) ||
+ (stop && strcmp(refname, stop->name) == 0)) {
+ /* zwarnnam(refname, "invalid self reference"); */
+ return stop ? (HashNode)pm : NULL;
+ }
+ if (*refname)
+ seek = refname;
+ }
+ }
+ else if (pm) {
+ if (!(stop && (stop->flags & PM_NAMEREF)))
+ return (HashNode)pm;
+ if (!(pm->node.flags & PM_NAMEREF))
+ return (pm->level < locallevel ? NULL : (HashNode)pm);
+ }
+ if (seek) {
+ queue_signals();
+ /* pm->width is the offset of any subscript */
+ if (pm && (pm->node.flags & PM_NAMEREF) && pm->width) {
+ if (stop) {
+ if (stop->flags & PM_NAMEREF)
+ hn = (HashNode)pm;
+ else
+ hn = NULL;
+ } else {
+ /* this has to be the end of any chain */
+ hn = (HashNode)pm; /* see fetchvalue() */
+ }
+ } else if ((hn = gethashnode2(realparamtab, seek))) {
+ if (pm) {
+ if (!(stop && (stop->flags & (PM_LOCAL)))) {
+ int scope = ((pm->node.flags & PM_NAMEREF) ?
+ ((pm->node.flags & PM_UPPER) ? -1 :
+ pm->base) : ((Param)hn)->level);
+ hn = (HashNode)upscope((Param)hn, scope);
+ }
+ /* user can't tag a nameref, safe for loop detection */
+ pm->node.flags |= PM_TAGGED;
+ }
+ if (hn) {
+ if (hn->flags & PM_AUTOLOAD)
+ hn = getparamnode(realparamtab, seek);
+ if (!(hn->flags & PM_UNSET))
+ hn = resolve_nameref((Param)hn, stop);
+ }
+ if (pm)
+ pm->node.flags &= ~PM_TAGGED;
+ } else if (stop && (stop->flags & PM_NAMEREF))
+ hn = (pm && (pm->node.flags & PM_NEWREF)) ? NULL : (HashNode)pm;
+ unqueue_signals();
+ }
+
+ return hn;
+}
+
+/**/
+mod_export void
+setloopvar(char *name, char *value)
+{
+ Param pm = (Param) gethashnode2(realparamtab, name);
+
+ if (pm && (pm->node.flags & PM_NAMEREF)) {
+ if (pm->node.flags & PM_READONLY) {
+ /* Bash error is: "%s: readonly variable" */
+ zerr("read-only reference: %s", pm->node.nam);
+ return;
+ }
+ pm->base = pm->width = 0;
+ SETREFNAME(pm, ztrdup(value));
+ pm->node.flags &= ~PM_UNSET;
+ pm->node.flags |= PM_NEWREF;
+ setscope(pm);
+ if (!errflag)
+ pm->node.flags &= ~PM_NEWREF;
+ } else
+ setsparam(name, ztrdup(value));
+}
+
+/**/
+static void
+setscope(Param pm)
+{
+ queue_signals();
+ if (pm->node.flags & PM_NAMEREF) do {
+ Param basepm;
+ struct asgment stop;
+ char *refname = GETREFNAME(pm);
+ char *t = refname ? itype_end(refname, INAMESPC, 0) : NULL;
+ int q = queue_signal_level();
+
+ /* Temporarily change nameref to array parameter itself */
+ if (t && *t == '[')
+ *t = 0;
+ else
+ t = 0;
+ stop.name = "";
+ stop.value.scalar = NULL;
+ stop.flags = PM_NAMEREF;
+ if (locallevel && !(pm->node.flags & PM_UPPER))
+ stop.flags |= PM_LOCAL;
+ dont_queue_signals(); /* Prevent unkillable loops */
+ basepm = (Param)resolve_nameref(pm, &stop);
+ restore_queue_signals(q);
+ if (t) {
+ pm->width = t - refname;
+ *t = '[';
+ }
+ if (basepm) {
+ if (basepm->node.flags & PM_NAMEREF) {
+ if (pm == basepm) {
+ if (pm->node.flags & PM_SELFREF) {
+ /* Loop signalled by resolve_nameref() */
+ if (upscope(pm, pm->base) == pm) {
+ zerr("%s: invalid self reference", refname);
+ unsetparam_pm(pm, 0, 1);
+ break;
+ }
+ pm->node.flags &= ~PM_SELFREF;
+ } else if (pm->base == pm->level) {
+ if (refname && *refname &&
+ strcmp(pm->node.nam, refname) == 0) {
+ zerr("%s: invalid self reference", refname);
+ unsetparam_pm(pm, 0, 1);
+ break;
+ }
+ }
+ } else if ((t = GETREFNAME(basepm))) {
+ if (basepm->base <= basepm->level &&
+ strcmp(pm->node.nam, t) == 0) {
+ zerr("%s: invalid self reference", refname);
+ unsetparam_pm(pm, 0, 1);
+ break;
+ }
+ }
+ } else
+ pm->base = basepm->level;
+ } else if (pm->base < locallevel && refname &&
+ (basepm = (Param)getparamnode(realparamtab, refname)))
+ pm->base = basepm->level;
+ if (pm->base > pm->level) {
+ if (EMULATION(EMULATE_KSH)) {
+ zerr("%s: global reference cannot refer to local variable",
+ pm->node.nam);
+ unsetparam_pm(pm, 0, 1);
+ } else if (isset(WARNNESTEDVAR))
+ zwarn("reference %s in enclosing scope set to local variable %s",
+ pm->node.nam, refname);
+ }
+ if (refname && upscope(pm, pm->base) == pm &&
+ strcmp(pm->node.nam, refname) == 0) {
+ zerr("%s: invalid self reference", refname);
+ unsetparam_pm(pm, 0, 1);
+ }
+ } while (0);
+ unqueue_signals();
+}
+
+/**/
+mod_export Param
+upscope(Param pm, int reflevel)
+{
+ Param up = pm->old;
+ while (up && up->level >= reflevel) {
+ if (reflevel < 0 && up->level < locallevel)
+ break;
+ pm = up;
+ up = up->old;
+ }
+ if (reflevel < 0 && locallevel > 0)
+ return pm->level == locallevel ? up : pm;
+ return pm;
+}
+
+/**/
+mod_export int
+valid_refname(char *val)
+{
+ char *t = itype_end(val, INAMESPC, 0);
+
+ if (idigit(*val))
+ return 0;
+ if (*t != 0) {
+ if (*t == '[') {
+ tokenize(t = dupstring(t+1));
+ while ((t = parse_subscript(t, 0, ']')) && *t++ == Outbrack) {
+ if (*t == Inbrack)
+ ++t;
+ else
+ break;
+ }
+ if (t && *t) {
+ /* zwarn("%s: stuff after subscript: %s", val, t); */
+ t = NULL;
+ }
+ } else if (t[1] || !(*t == '!' || *t == '?' ||
+ *t == '$' || *t == '-' ||
+ *t == '0' || *t == '_')) {
+ /* Skipping * @ # because of doshfunc() implementation */
+ t = NULL;
+ }
+ }
+ return !!t;
+}
diff --git a/Src/parse.c b/Src/parse.c
index d612b7e17..8edc701f4 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -120,7 +120,7 @@ struct heredocs *hdocs;
* - if not (type & Z_END), followed by next WC_LIST
*
* WC_SUBLIST
- * - data contains type (&&, ||, END) and flags (coprog, not)
+ * - data contains type (&&, ||, END) and flags (coproc, not)
* - followed by code for sublist
* - if not (type == END), followed by next WC_SUBLIST
*
@@ -433,9 +433,9 @@ ecstrcode(char *s)
t = has_token(s);
wordcode c = (t ? 3 : 2);
switch (l) {
- case 4: c |= ((wordcode) STOUC(s[2])) << 19;
- case 3: c |= ((wordcode) STOUC(s[1])) << 11;
- case 2: c |= ((wordcode) STOUC(s[0])) << 3; break;
+ case 4: c |= ((wordcode) (unsigned char) s[2]) << 19;
+ case 3: c |= ((wordcode) (unsigned char) s[1]) << 11;
+ case 2: c |= ((wordcode) (unsigned char) s[0]) << 3; break;
case 1: c = (t ? 7 : 6); break;
}
return c;
@@ -490,7 +490,7 @@ init_parse_status(void)
/*
* These variables are currently declared by the parser, so we
* initialise them here. Possibly they are more naturally declared
- * by the lexical anaylser; however, as they are used for signalling
+ * by the lexical analyser; however, as they are used for signalling
* between the two it's a bit ambiguous. We clear them when
* using the lexical analyser for strings as well as here.
*/
@@ -601,7 +601,7 @@ clear_hdocs(void)
* | sublist [ SEPER | AMPER | AMPERBANG ]
*
* cmdsubst indicates our event is part of a command-style
- * substitution terminated by the token indicationg, usual closing
+ * substitution terminated by the token indicated, usually closing
* parenthesis. In other cases endtok is ENDINPUT.
*/
@@ -1935,6 +1935,8 @@ par_simple(int *cmplx, int nr)
if (*ptr == Outbrace && ptr > tokstr + 1)
{
+ /* Should we allow namespace FDs, {.foo.bar}>&file ? *
+ * If so, change IIDENT to INAMESPC here */
if (itype_end(tokstr+1, IIDENT, 0) >= ptr)
{
char *toksave = tokstr;
@@ -2055,6 +2057,9 @@ par_simple(int *cmplx, int nr)
if (isset(EXECOPT) && hasalias && !isset(ALIASFUNCDEF) && argc &&
hasalias != input_hasalias()) {
zwarn("defining function based on alias `%s'", hasalias);
+ herrflush();
+ if (noerrs != 2)
+ errflag |= ERRFLAG_ERROR;
YYERROR(oecused);
}
@@ -2248,6 +2253,9 @@ par_redir(int *rp, char *idstring)
struct heredocs **hd;
int htype = type;
+ if (strchr(tokstr, '\n'))
+ YYERROR(ecused);
+
/*
* Add two here for the string to remember the HERE
* terminator in raw and munged form.
@@ -2384,7 +2392,7 @@ par_nl_wordlist(void)
*/
/**/
-void (*condlex) _((void)) = zshlex;
+void (*condlex) (void) = zshlex;
/*
* cond : cond_1 { SEPER } [ DBAR { SEPER } cond ]
@@ -2722,11 +2730,10 @@ yyerror(int noerr)
if (!t || !t[t0] || t[t0] == '\n')
break;
if (!(histdone & HISTFLAG_NOEXEC) && !(errflag & ERRFLAG_INT)) {
- if (t0 == 20)
- zwarn("parse error near `%l...'", t, 20);
- else if (t0)
- zwarn("parse error near `%l'", t, t0);
- else
+ if (t0) {
+ t = metafy(t, t0, META_STATIC);
+ zwarn("parse error near `%s%s'", t, t0 == 20 ? "..." : "");
+ } else
zwarn("parse error");
}
if (!noerr && noerrs != 2)
@@ -3211,12 +3218,14 @@ bin_zcompile(char *nam, char **args, Options ops, UNUSED(int func))
if (!args[1] && !(OPT_ISSET(ops,'c') || OPT_ISSET(ops,'a'))) {
queue_signals();
- ret = build_dump(nam, dyncat(*args, FD_EXT), args, OPT_ISSET(ops,'U'),
+ dump = unmetafy(dyncat(*args, FD_EXT), NULL);
+ ret = build_dump(nam, dump, args, OPT_ISSET(ops,'U'),
map, flags);
unqueue_signals();
return ret;
}
- dump = (strsfx(FD_EXT, *args) ? *args : dyncat(*args, FD_EXT));
+ dump = (strsfx(FD_EXT, *args) ? dupstring(*args) : dyncat(*args, FD_EXT));
+ unmetafy(dump, NULL);
queue_signals();
ret = ((OPT_ISSET(ops,'c') || OPT_ISSET(ops,'a')) ?
@@ -3394,6 +3403,7 @@ build_dump(char *nam, char *dump, char **files, int ali, int map, int flags)
for (hlen = FD_PRELEN, tlen = 0; *files; files++) {
struct stat st;
+ char *fnam;
if (check_cond(*files, "k")) {
flags = (flags & ~(FDHF_KSHLOAD | FDHF_ZSHLOAD)) | FDHF_KSHLOAD;
@@ -3402,7 +3412,8 @@ build_dump(char *nam, char *dump, char **files, int ali, int map, int flags)
flags = (flags & ~(FDHF_KSHLOAD | FDHF_ZSHLOAD)) | FDHF_ZSHLOAD;
continue;
}
- if ((fd = open(*files, O_RDONLY)) < 0 ||
+ fnam = unmeta(*files);
+ if ((fd = open(fnam, O_RDONLY)) < 0 ||
fstat(fd, &st) != 0 || !S_ISREG(st.st_mode) ||
(flen = lseek(fd, 0, 2)) == -1) {
if (fd >= 0)
diff --git a/Src/pattern.c b/Src/pattern.c
index e947d1216..1e0ae88d9 100644
--- a/Src/pattern.c
+++ b/Src/pattern.c
@@ -239,7 +239,7 @@ typedef unsigned long zrange_t;
* a bit tricky...
*/
#define WCHAR_INVALID(ch) \
- ((wchar_t) (0xDC00 + STOUC(ch)))
+ ((wchar_t) (0xDC00 + (unsigned char) ch))
#endif /* MULTIBYTE_SUPPORT */
/*
@@ -346,7 +346,7 @@ metacharinc(char **x)
* set doesn't have the property that all bytes with the 8th
* bit clear are single characters then we are stuffed.
*/
- if (!(patglobflags & GF_MULTIBYTE) || !(STOUC(*inptr) & 0x80))
+ if (!(patglobflags & GF_MULTIBYTE) || !((unsigned char) *inptr & 0x80))
{
if (itok(*inptr))
inchar = ztokens[*inptr++ - Pound];
@@ -357,7 +357,7 @@ metacharinc(char **x)
inchar = *inptr++;
}
*x = inptr;
- return (wchar_t)STOUC(inchar);
+ return (wchar_t)(unsigned char) inchar;
}
while (*inptr) {
@@ -1181,8 +1181,8 @@ pattern_range_to_string(char *rangestr, char *outstr)
int len = 0;
while (*rangestr) {
- if (imeta(STOUC(*rangestr))) {
- int swtype = STOUC(*rangestr) - STOUC(Meta);
+ if (imeta((unsigned char) *rangestr)) {
+ int swtype = (unsigned char) *rangestr - (unsigned char) Meta;
if (swtype == 0) {
/* Ordindary metafied character */
@@ -1278,17 +1278,17 @@ patcomppiece(int *flagp, int paren)
kshchar = '\0';
if (*patparse && patparse[1] == Inpar) {
if (*patparse == zpc_special[ZPC_KSH_PLUS])
- kshchar = STOUC('+');
+ kshchar = (unsigned char) '+';
else if (*patparse == zpc_special[ZPC_KSH_BANG])
- kshchar = STOUC('!');
+ kshchar = (unsigned char) '!';
else if (*patparse == zpc_special[ZPC_KSH_BANG2])
- kshchar = STOUC('!');
+ kshchar = (unsigned char) '!';
else if (*patparse == zpc_special[ZPC_KSH_AT])
- kshchar = STOUC('@');
+ kshchar = (unsigned char) '@';
else if (*patparse == zpc_special[ZPC_KSH_STAR])
- kshchar = STOUC('*');
+ kshchar = (unsigned char) '*';
else if (*patparse == zpc_special[ZPC_KSH_QUEST])
- kshchar = STOUC('?');
+ kshchar = (unsigned char) '?';
}
/*
@@ -1468,7 +1468,8 @@ patcomppiece(int *flagp, int paren)
ch = range_type(patparse, len);
patparse = nptr + 2;
if (ch != PP_UNKWN)
- patadd(NULL, STOUC(Meta) + ch, 1, PA_NOALIGN);
+ patadd(NULL, (unsigned char) Meta + ch, 1,
+ PA_NOALIGN);
continue;
}
charstart = patparse;
@@ -1476,10 +1477,10 @@ patcomppiece(int *flagp, int paren)
if (*patparse == Dash && patparse[1] &&
patparse[1] != Outbrack) {
- patadd(NULL, STOUC(Meta)+PP_RANGE, 1, PA_NOALIGN);
+ patadd(NULL, (unsigned char) Meta+PP_RANGE, 1, PA_NOALIGN);
if (itok(*charstart)) {
- patadd(0, STOUC(ztokens[*charstart - Pound]), 1,
- PA_NOALIGN);
+ patadd(0, (unsigned char) ztokens[*charstart - Pound],
+ 1, PA_NOALIGN);
} else {
patadd(charstart, 0, patparse-charstart, PA_NOALIGN);
}
@@ -1487,7 +1488,7 @@ patcomppiece(int *flagp, int paren)
METACHARINC(patparse);
}
if (itok(*charstart)) {
- patadd(0, STOUC(ztokens[*charstart - Pound]), 1,
+ patadd(0, (unsigned char) ztokens[*charstart - Pound], 1,
PA_NOALIGN);
} else {
patadd(charstart, 0, patparse-charstart, PA_NOALIGN);
@@ -1910,8 +1911,8 @@ charref(char *x, char *y, int *zmb_ind)
wchar_t wc;
size_t ret;
- if (!(patglobflags & GF_MULTIBYTE) || !(STOUC(*x) & 0x80))
- return (wchar_t) STOUC(*x);
+ if (!(patglobflags & GF_MULTIBYTE) || !((unsigned char) *x & 0x80))
+ return (wchar_t) (unsigned char) *x;
ret = mbrtowc(&wc, x, y-x, &shiftstate);
@@ -1937,7 +1938,7 @@ charnext(char *x, char *y)
wchar_t wc;
size_t ret;
- if (!(patglobflags & GF_MULTIBYTE) || !(STOUC(*x) & 0x80))
+ if (!(patglobflags & GF_MULTIBYTE) || !((unsigned char) *x & 0x80))
return x + 1;
ret = mbrtowc(&wc, x, y-x, &shiftstate);
@@ -1965,8 +1966,8 @@ charrefinc(char **x, char *y, int *z)
wchar_t wc;
size_t ret;
- if (!(patglobflags & GF_MULTIBYTE) || !(STOUC(**x) & 0x80))
- return (wchar_t) STOUC(*(*x)++);
+ if (!(patglobflags & GF_MULTIBYTE) || !((unsigned char) **x & 0x80))
+ return (wchar_t) (unsigned char) *(*x)++;
ret = mbrtowc(&wc, *x, y-*x, &shiftstate);
@@ -2025,13 +2026,13 @@ charsub(char *x, char *y)
#else /* no MULTIBYTE_SUPPORT */
/* Get a character from the start point in a string */
-#define CHARREF(x, y) (STOUC(*(x)))
+#define CHARREF(x, y) ((unsigned char) (*(x)))
/* Get a pointer to the next character */
#define CHARNEXT(x, y) ((x)+1)
/* Increment a pointer past the current character. */
#define CHARINC(x, y) ((x)++)
/* Get a character and increment */
-#define CHARREFINC(x, y, z) (STOUC(*(x)++))
+#define CHARREFINC(x, y, z) ((unsigned char) (*(x)++))
/* Counter the number of characters between two pointers, smaller first */
#define CHARSUB(x,y) ((y) - (x))
@@ -2890,7 +2891,7 @@ patmatch(Upat prog)
}
if (!no && P_OP(next) == P_EXACTLY &&
(!P_LS_LEN(next) ||
- !idigit(STOUC(*P_LS_STR(next)))) &&
+ !idigit((unsigned char) (*P_LS_STR(next)))) &&
!(patglobflags & 0xff))
return 0;
patinput = --save;
@@ -2986,14 +2987,15 @@ patmatch(Upat prog)
case P_EXCSYNC:
/* See the P_EXCLUDE code below for where syncptr comes from */
{
- unsigned char *syncptr;
+ unsigned char *syncstart, *syncptr, *ptr;
Upat after;
after = P_OPERAND(scan);
DPUTS(!P_ISEXCLUDE(after),
"BUG: EXCSYNC not followed by EXCLUDE.");
DPUTS(!P_OPERAND(after)->p,
"BUG: EXCSYNC not handled by EXCLUDE");
- syncptr = P_OPERAND(after)->p + (patinput - patinstart);
+ syncstart = P_OPERAND(after)->p;
+ syncptr = syncstart + (patinput - patinstart);
/*
* If we already matched from here, this time we fail.
* See WBRANCH code for story about error count.
@@ -3008,6 +3010,23 @@ patmatch(Upat prog)
* failed anyway.
*/
*syncptr = errsfound + 1;
+ /*
+ * Because of backtracking, any match before this point
+ * can't apply to the current branch we're on so is now
+ * a failure --- this can happen if, on a previous
+ * branch, we initially marked a success before failing
+ * on a later part of the pattern after marking up the
+ * P_EXCSYNC (even an end anchor will have this effect).
+ * To make sure we record the current match point
+ * correctly, mark those down now.
+ *
+ * This might have side effects on the efficiency of
+ * pathological cases involving nested branches. To
+ * fix that we'd probably need to record matches on
+ * different branches separately.
+ */
+ for (ptr = syncstart; ptr < syncptr; ++ptr)
+ *ptr = 0;
}
break;
case P_EXCEND:
@@ -3600,8 +3619,8 @@ mb_patmatchrange(char *range, wchar_t ch, int zmb_ind, wint_t *indptr, int *mtp)
* ranges specially.
*/
while (*range) {
- if (imeta(STOUC(*range))) {
- int swtype = STOUC(*range++) - STOUC(Meta);
+ if (imeta((unsigned char) *range)) {
+ int swtype = (unsigned char) *range++ - (unsigned char) Meta;
if (mtp)
*mtp = swtype;
switch (swtype) {
@@ -3672,6 +3691,7 @@ mb_patmatchrange(char *range, wchar_t ch, int zmb_ind, wint_t *indptr, int *mtp)
return 1;
break;
case PP_IDENT:
+ /* Could use INAMESPC here? */
if (wcsitype(ch, IIDENT))
return 1;
break;
@@ -3753,8 +3773,8 @@ mb_patmatchindex(char *range, wint_t ind, wint_t *chr, int *mtp)
*mtp = 0;
while (*range) {
- if (imeta(STOUC(*range))) {
- int swtype = STOUC(*range++) - STOUC(Meta);
+ if (imeta((unsigned char) *range)) {
+ int swtype = (unsigned char) *range++ - (unsigned char) Meta;
switch (swtype) {
case 0:
range--;
@@ -3845,13 +3865,13 @@ patmatchrange(char *range, int ch, int *indptr, int *mtp)
* ranges specially.
*/
for (; *range; range++) {
- if (imeta(STOUC(*range))) {
- int swtype = STOUC(*range) - STOUC(Meta);
+ if (imeta((unsigned char) *range)) {
+ int swtype = (unsigned char) *range - (unsigned char) Meta;
if (mtp)
*mtp = swtype;
switch (swtype) {
case 0:
- if (STOUC(*++range ^ 32) == ch)
+ if ((unsigned char) (*++range ^ 32) == ch)
return 1;
break;
case PP_ALPHA:
@@ -3931,9 +3951,9 @@ patmatchrange(char *range, int ch, int *indptr, int *mtp)
break;
case PP_RANGE:
range++;
- r1 = STOUC(UNMETA(range));
+ r1 = (unsigned char) UNMETA(range);
METACHARINC(range);
- r2 = STOUC(UNMETA(range));
+ r2 = (unsigned char) UNMETA(range);
if (*range == Meta)
range++;
if (r1 <= ch && ch <= r2) {
@@ -3955,7 +3975,7 @@ patmatchrange(char *range, int ch, int *indptr, int *mtp)
DPUTS(1, "BUG: unknown metacharacter in range.");
break;
}
- } else if (STOUC(*range) == ch) {
+ } else if ((unsigned char) *range == ch) {
if (mtp)
*mtp = 0;
return 1;
@@ -3989,12 +4009,12 @@ patmatchindex(char *range, int ind, int *chr, int *mtp)
*mtp = 0;
for (; *range; range++) {
- if (imeta(STOUC(*range))) {
- int swtype = STOUC(*range) - STOUC(Meta);
+ if (imeta((unsigned char) *range)) {
+ int swtype = (unsigned char) *range - (unsigned char) Meta;
switch (swtype) {
case 0:
/* ordinary metafied character */
- rchr = STOUC(*++range) ^ 32;
+ rchr = (unsigned char) *++range ^ 32;
if (!ind) {
*chr = rchr;
return 1;
@@ -4028,9 +4048,9 @@ patmatchindex(char *range, int ind, int *chr, int *mtp)
case PP_RANGE:
range++;
- r1 = STOUC(UNMETA(range));
+ r1 = (unsigned char) UNMETA(range);
METACHARINC(range);
- r2 = STOUC(UNMETA(range));
+ r2 = (unsigned char) UNMETA(range);
if (*range == Meta)
range++;
rdiff = r2 - r1;
@@ -4050,7 +4070,7 @@ patmatchindex(char *range, int ind, int *chr, int *mtp)
}
} else {
if (!ind) {
- *chr = STOUC(*range);
+ *chr = (unsigned char) *range;
return 1;
}
}
diff --git a/Src/prompt.c b/Src/prompt.c
index 092de63a4..7467cdfb9 100644
--- a/Src/prompt.c
+++ b/Src/prompt.c
@@ -30,10 +30,20 @@
#include "zsh.mdh"
#include "prompt.pro"
-/* text attribute mask */
+/* current text attributes */
/**/
-mod_export zattr txtattrmask;
+mod_export zattr txtcurrentattrs;
+
+/* pending changes for attributes */
+
+/**/
+mod_export zattr txtpendingattrs;
+
+/* mask of attributes with an unknown state */
+
+/**/
+mod_export zattr txtunknownattrs;
/* the command stack for use with %_ in prompts */
@@ -160,15 +170,11 @@ promptpath(char *p, int npath, int tilde)
* between spacing and non-spacing parts of the prompt, and
* Nularg, which (in a non-spacing sequence) indicates a
* `glitch' space.
- *
- * txtchangep gives an integer controlling the attributes of
- * the prompt. This is for use in zle to maintain the attributes
- * consistently. Other parts of the shell should not need to use it.
*/
/**/
mod_export char *
-promptexpand(char *s, int ns, char *rs, char *Rs, zattr *txtchangep)
+promptexpand(char *s, int ns, char *rs, char *Rs)
{
struct buf_vars new_vars;
@@ -212,7 +218,7 @@ promptexpand(char *s, int ns, char *rs, char *Rs, zattr *txtchangep)
new_vars.bp1 = NULL;
new_vars.truncwidth = 0;
- putpromptchar(1, '\0', txtchangep);
+ putpromptchar(1, '\0');
addbufspc(2);
if (new_vars.dontcount)
*new_vars.bp++ = Outpar;
@@ -235,6 +241,68 @@ promptexpand(char *s, int ns, char *rs, char *Rs, zattr *txtchangep)
return new_vars.buf;
}
+/* Get the escape sequence for a given attribute. */
+/**/
+mod_export char *
+zattrescape(zattr atr, int *len)
+{
+ struct buf_vars new_vars;
+ zattr savecurrent = txtcurrentattrs;
+ zattr saveunknown = txtunknownattrs;
+
+ memset(&new_vars, 0, sizeof(new_vars));
+ new_vars.last = bv;
+ bv = &new_vars;
+ new_vars.bufspc = 256;
+ new_vars.bp = new_vars.bufline = new_vars.buf = zshcalloc(new_vars.bufspc);
+ new_vars.dontcount = 1;
+
+ txtunknownattrs = 0;
+ treplaceattrs(atr);
+ applytextattributes(TSC_PROMPT);
+
+ bv = new_vars.last;
+
+ txtpendingattrs = txtcurrentattrs = savecurrent;
+ txtunknownattrs = saveunknown;
+
+ return unmetafy(new_vars.buf, len);
+}
+
+/* Parse the argument for %H */
+/**/
+mod_export char *
+parsehighlight(char *arg, char endchar, zattr *atr)
+{
+ static int entered = 0;
+ char *var = ".zle.hlgroups";
+ struct value vbuf;
+ Value v;
+ char *ep, *attrs;
+ if ((ep = strchr(arg, endchar)))
+ *ep = '\0';
+ if (!entered && (v = getvalue(&vbuf, &var, 0)) &&
+ PM_TYPE(v->pm->node.flags) == PM_HASHED)
+ {
+ Param node;
+ HashTable ht = v->pm->gsu.h->getfn(v->pm);
+ if (ht && (node = (Param) ht->getnode(ht, arg))) {
+ attrs = node->gsu.s->getfn(node);
+ entered = 1;
+ if (match_highlight(attrs, atr, 0) == attrs)
+ *atr = TXT_ERROR;
+ } else
+ *atr = TXT_ERROR;
+ } else
+ *atr = TXT_ERROR;
+ if (ep)
+ *ep++ = endchar;
+ else
+ ep = strchr(arg, '\0');
+ entered = 0;
+ return ep;
+}
+
/* Parse the argument for %F and %K */
static zattr
parsecolorchar(zattr arg, int is_fg)
@@ -253,7 +321,7 @@ parsecolorchar(zattr arg, int is_fg)
*ep = '\0';
/* expand the contents of the argument so you can use
* %v for example */
- coll = col = promptexpand(bv->fm, 0, NULL, NULL, NULL);
+ coll = col = promptexpand(bv->fm, 0, NULL, NULL);
*ep = oc;
arg = match_colour((const char **)&coll, is_fg, 0);
free(col);
@@ -278,7 +346,7 @@ parsecolorchar(zattr arg, int is_fg)
/**/
static int
-putpromptchar(int doprint, int endchar, zattr *txtchangep)
+putpromptchar(int doprint, int endchar)
{
char *ss, *hostnam;
int t0, arg, test, sep, j, numjobs, len;
@@ -401,7 +469,7 @@ putpromptchar(int doprint, int endchar, zattr *txtchangep)
test = 1;
break;
case 'S':
- if (time(NULL) - shtimer.tv_sec >= arg)
+ if (zmonotime(NULL) - shtimer.tv_sec >= arg)
test = 1;
break;
case 'v':
@@ -430,10 +498,9 @@ putpromptchar(int doprint, int endchar, zattr *txtchangep)
/* Don't do the current truncation until we get back */
otruncwidth = bv->truncwidth;
bv->truncwidth = 0;
- if (!putpromptchar(test == 1 && doprint, sep,
- txtchangep) || !*++bv->fm ||
- !putpromptchar(test == 0 && doprint, ')',
- txtchangep)) {
+ if (!putpromptchar(test == 1 && doprint, sep) ||
+ !*++bv->fm ||
+ !putpromptchar(test == 0 && doprint, ')')) {
bv->truncwidth = otruncwidth;
return 0;
}
@@ -488,8 +555,7 @@ putpromptchar(int doprint, int endchar, zattr *txtchangep)
if (jobtab[j].stat && jobtab[j].procs &&
!(jobtab[j].stat & STAT_NOPRINT)) numjobs++;
addbufspc(DIGBUFSIZE);
- sprintf(bv->bp, "%d", numjobs);
- bv->bp += strlen(bv->bp);
+ bv->bp += sprintf(bv->bp, "%d", numjobs);
break;
case 'M':
queue_signals();
@@ -519,71 +585,67 @@ putpromptchar(int doprint, int endchar, zattr *txtchangep)
unqueue_signals();
break;
case 'S':
- txtchangeset(txtchangep, TXTSTANDOUT, TXTNOSTANDOUT);
- txtset(TXTSTANDOUT);
- tsetcap(TCSTANDOUTBEG, TSC_PROMPT);
+ tsetattrs(TXTSTANDOUT);
+ applytextattributes(TSC_PROMPT);
break;
case 's':
- txtchangeset(txtchangep, TXTNOSTANDOUT, TXTSTANDOUT);
- txtunset(TXTSTANDOUT);
- tsetcap(TCSTANDOUTEND, TSC_PROMPT|TSC_DIRTY);
+ tunsetattrs(TXTSTANDOUT);
+ applytextattributes(TSC_PROMPT);
break;
case 'B':
- txtchangeset(txtchangep, TXTBOLDFACE, TXTNOBOLDFACE);
- txtset(TXTBOLDFACE);
- tsetcap(TCBOLDFACEBEG, TSC_PROMPT|TSC_DIRTY);
+ tsetattrs(TXTBOLDFACE);
+ applytextattributes(TSC_PROMPT);
break;
case 'b':
- txtchangeset(txtchangep, TXTNOBOLDFACE, TXTBOLDFACE);
- txtunset(TXTBOLDFACE);
- tsetcap(TCALLATTRSOFF, TSC_PROMPT|TSC_DIRTY);
+ tunsetattrs(TXTBOLDFACE);
+ applytextattributes(TSC_PROMPT);
break;
case 'U':
- txtchangeset(txtchangep, TXTUNDERLINE, TXTNOUNDERLINE);
- txtset(TXTUNDERLINE);
- tsetcap(TCUNDERLINEBEG, TSC_PROMPT);
+ tsetattrs(TXTUNDERLINE);
+ applytextattributes(TSC_PROMPT);
break;
case 'u':
- txtchangeset(txtchangep, TXTNOUNDERLINE, TXTUNDERLINE);
- txtunset(TXTUNDERLINE);
- tsetcap(TCUNDERLINEEND, TSC_PROMPT|TSC_DIRTY);
+ tunsetattrs(TXTUNDERLINE);
+ applytextattributes(TSC_PROMPT);
break;
case 'F':
atr = parsecolorchar(arg, 1);
- if (!(atr & (TXT_ERROR | TXTNOFGCOLOUR))) {
- txtchangeset(txtchangep, atr & TXT_ATTR_FG_ON_MASK,
- TXTNOFGCOLOUR | TXT_ATTR_FG_COL_MASK);
- txtunset(TXT_ATTR_FG_COL_MASK);
- txtset(atr & TXT_ATTR_FG_ON_MASK);
- set_colour_attribute(atr, COL_SEQ_FG, TSC_PROMPT);
+ if (atr && atr != TXT_ERROR) {
+ tsetattrs(atr);
+ applytextattributes(TSC_PROMPT);
break;
}
/* else FALLTHROUGH */
case 'f':
- txtchangeset(txtchangep, TXTNOFGCOLOUR, TXT_ATTR_FG_ON_MASK);
- txtunset(TXT_ATTR_FG_ON_MASK);
- set_colour_attribute(TXTNOFGCOLOUR, COL_SEQ_FG, TSC_PROMPT);
+ tunsetattrs(TXTFGCOLOUR);
+ applytextattributes(TSC_PROMPT);
break;
case 'K':
atr = parsecolorchar(arg, 0);
- if (!(atr & (TXT_ERROR | TXTNOBGCOLOUR))) {
- txtchangeset(txtchangep, atr & TXT_ATTR_BG_ON_MASK,
- TXTNOBGCOLOUR | TXT_ATTR_BG_COL_MASK);
- txtunset(TXT_ATTR_BG_COL_MASK);
- txtset(atr & TXT_ATTR_BG_ON_MASK);
- set_colour_attribute(atr, COL_SEQ_BG, TSC_PROMPT);
+ if (atr && atr != TXT_ERROR) {
+ tsetattrs(atr);
+ applytextattributes(TSC_PROMPT);
break;
}
/* else FALLTHROUGH */
case 'k':
- txtchangeset(txtchangep, TXTNOBGCOLOUR, TXT_ATTR_BG_ON_MASK);
- txtunset(TXT_ATTR_BG_ON_MASK);
- set_colour_attribute(TXTNOBGCOLOUR, COL_SEQ_BG, TSC_PROMPT);
+ tunsetattrs(TXTBGCOLOUR);
+ applytextattributes(TSC_PROMPT);
+ break;
+ case 'H':
+ if (bv->fm[1] == '{') {
+ bv->fm = parsehighlight(bv->fm + 2, '}', &atr);
+ --bv->fm;
+ if (atr != TXT_ERROR) {
+ treplaceattrs(atr);
+ applytextattributes(TSC_PROMPT);
+ }
+ }
break;
case '[':
if (idigit(*++bv->fm))
arg = zstrtol(bv->fm, &bv->fm, 10);
- if (!prompttrunc(arg, ']', doprint, endchar, txtchangep))
+ if (!prompttrunc(arg, ']', doprint, endchar))
return *bv->fm;
break;
case '<':
@@ -596,7 +658,7 @@ putpromptchar(int doprint, int endchar, zattr *txtchangep)
if (arg <= 0)
arg = 1;
}
- if (!prompttrunc(arg, *bv->fm, doprint, endchar, txtchangep))
+ if (!prompttrunc(arg, *bv->fm, doprint, endchar))
return *bv->fm;
break;
case '{': /*}*/
@@ -719,20 +781,18 @@ putpromptchar(int doprint, int endchar, zattr *txtchangep)
case 'L':
addbufspc(DIGBUFSIZE);
#if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD)
- sprintf(bv->bp, "%lld", shlvl);
+ bv->bp += sprintf(bv->bp, "%lld", shlvl);
#else
- sprintf(bv->bp, "%ld", (long)shlvl);
+ bv->bp += sprintf(bv->bp, "%ld", (long)shlvl);
#endif
- bv->bp += strlen(bv->bp);
break;
case '?':
addbufspc(DIGBUFSIZE);
#if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD)
- sprintf(bv->bp, "%lld", lastval);
+ bv->bp += sprintf(bv->bp, "%lld", lastval);
#else
- sprintf(bv->bp, "%ld", (long)lastval);
+ bv->bp += sprintf(bv->bp, "%ld", (long)lastval);
#endif
- bv->bp += strlen(bv->bp);
break;
case '%':
case ')':
@@ -823,8 +883,7 @@ putpromptchar(int doprint, int endchar, zattr *txtchangep)
fsptr = fsptr->prev;
}
addbufspc(DIGBUFSIZE);
- sprintf(bv->bp, "%d", depth);
- bv->bp += strlen(bv->bp);
+ bv->bp += sprintf(bv->bp, "%d", depth);
break;
}
case 'I':
@@ -841,11 +900,10 @@ putpromptchar(int doprint, int endchar, zattr *txtchangep)
lineno--;
addbufspc(DIGBUFSIZE);
#if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD)
- sprintf(bv->bp, "%lld", flineno);
+ bv->bp += sprintf(bv->bp, "%lld", flineno);
#else
- sprintf(bv->bp, "%ld", (long)flineno);
+ bv->bp += sprintf(bv->bp, "%ld", (long)flineno);
#endif
- bv->bp += strlen(bv->bp);
break;
}
/* else we're in a file and lineno is already correct */
@@ -853,11 +911,10 @@ putpromptchar(int doprint, int endchar, zattr *txtchangep)
case 'i':
addbufspc(DIGBUFSIZE);
#if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD)
- sprintf(bv->bp, "%lld", lineno);
+ bv->bp += sprintf(bv->bp, "%lld", lineno);
#else
- sprintf(bv->bp, "%ld", (long)lineno);
+ bv->bp += sprintf(bv->bp, "%ld", (long)lineno);
#endif
- bv->bp += strlen(bv->bp);
break;
case 'x':
if (funcstack && funcstack->tp != FS_SOURCE &&
@@ -1013,9 +1070,8 @@ stradd(char *d)
mod_export void
tsetcap(int cap, int flags)
{
- if (tccan(cap) && !isset(SINGLELINEZLE) &&
- !(termflags & (TERM_NOUP|TERM_BAD|TERM_UNKNOWN))) {
- switch (flags & TSC_OUTPUT_MASK) {
+ if (tccan(cap) && !(termflags & (TERM_NOUP|TERM_BAD|TERM_UNKNOWN))) {
+ switch (flags) {
case TSC_RAW:
tputs(tcstr[cap], 1, putraw);
break;
@@ -1045,20 +1101,6 @@ tsetcap(int cap, int flags)
}
break;
}
-
- if (flags & TSC_DIRTY) {
- flags &= ~TSC_DIRTY;
- if (txtisset(TXTBOLDFACE) && cap != TCBOLDFACEBEG)
- tsetcap(TCBOLDFACEBEG, flags);
- if (txtisset(TXTSTANDOUT))
- tsetcap(TCSTANDOUTBEG, flags);
- if (txtisset(TXTUNDERLINE))
- tsetcap(TCUNDERLINEBEG, flags);
- if (txtisset(TXTFGCOLOUR))
- set_colour_attribute(txtattrmask, COL_SEQ_FG, flags);
- if (txtisset(TXTBGCOLOUR))
- set_colour_attribute(txtattrmask, COL_SEQ_BG, flags);
- }
}
}
@@ -1219,8 +1261,7 @@ countprompt(char *str, int *wp, int *hp, int overf)
/**/
static int
-prompttrunc(int arg, int truncchar, int doprint, int endchar,
- zattr *txtchangep)
+prompttrunc(int arg, int truncchar, int doprint, int endchar)
{
if (arg > 0) {
char ch = *bv->fm, *ptr, *truncstr;
@@ -1267,7 +1308,7 @@ prompttrunc(int arg, int truncchar, int doprint, int endchar,
w = bv->bp - bv->buf;
bv->fm++;
bv->trunccount = bv->dontcount;
- putpromptchar(doprint, endchar, txtchangep);
+ putpromptchar(doprint, endchar);
bv->trunccount = 0;
ptr = bv->buf + w; /* putpromptchar() may have realloc()'d */
*bv->bp = '\0';
@@ -1547,7 +1588,7 @@ prompttrunc(int arg, int truncchar, int doprint, int endchar,
* With bv->truncwidth set to zero, we always reach endchar *
* (or the terminating NULL) this time round. *
*/
- if (!putpromptchar(doprint, endchar, txtchangep))
+ if (!putpromptchar(doprint, endchar))
return 0;
}
/* Now we have to trick it into matching endchar again */
@@ -1585,6 +1626,158 @@ cmdpop(void)
cmdsp--;
}
+/* functions for handling attributes */
+
+/**/
+mod_export void
+applytextattributes(int flags)
+{
+ zattr change = txtcurrentattrs ^ txtpendingattrs;
+ zattr keepon = ~change & txtpendingattrs & TXT_ATTR_ALL;
+ zattr turnoff = change & ~txtpendingattrs & TXT_ATTR_ALL;
+ int keepcount, turncount = 0;
+
+ /* bail out early if we wouldn't do anything */
+ if (!change)
+ return;
+
+ if (txtunknownattrs) {
+ txtunknownattrs &= ~change; /* changes cease to be unknown */
+ /* can't turn unknown attrs back on so avoid wiping them */
+ keepcount = 1;
+ } else {
+ /* If we want to turn off more attributes than we want to keep on
+ * then it takes fewer termcap sequences to just turn off all the
+ * attributes. */
+ for (keepcount = 0; keepon; keepcount++) /* count bits */
+ keepon &= keepon - 1;
+ for (; turnoff; turncount++)
+ turnoff &= turnoff - 1;
+ }
+
+ /* enabling bold can be relied upon to disable faint
+ * (the converse not so as that commonly does nothing at all) */
+ if (txtcurrentattrs & TXTFAINT && txtpendingattrs & TXTBOLDFACE) {
+ --turncount;
+ change &= ~TXTFAINT;
+ }
+
+ if (keepcount < turncount ||
+ (change & ~txtpendingattrs & TXT_ATTR_FONT_WEIGHT)) {
+ tsetcap(TCALLATTRSOFF, flags);
+ /* this cleared all attributes, may need to restore some */
+ change = txtpendingattrs & TXT_ATTR_ALL & ~txtunknownattrs;
+ txtunknownattrs = 0;
+ } else {
+ if (change & ~txtpendingattrs & TXTSTANDOUT) {
+ tsetcap(TCSTANDOUTEND, flags);
+ /* in some cases, that clears all attributes */
+ change = (txtpendingattrs & TXT_ATTR_ALL & ~txtunknownattrs) |
+ (TXTUNDERLINE & change);
+ }
+ if (change & ~txtpendingattrs & TXTUNDERLINE) {
+ tsetcap(TCUNDERLINEEND, flags);
+ /* in some cases, that clears all attributes */
+ change = txtpendingattrs & TXT_ATTR_ALL & ~txtunknownattrs;
+ }
+ if (change & ~txtpendingattrs & TXTITALIC)
+ tsetcap(TCITALICSEND, flags);
+ }
+ if (change & txtpendingattrs & TXTBOLDFACE)
+ tsetcap(TCBOLDFACEBEG, flags);
+ if (change & txtpendingattrs & TXTFAINT)
+ tsetcap(TCFAINTBEG, flags);
+ if (change & txtpendingattrs & TXTSTANDOUT)
+ tsetcap(TCSTANDOUTBEG, flags);
+ if (change & txtpendingattrs & TXTUNDERLINE)
+ tsetcap(TCUNDERLINEBEG, flags);
+ if (change & txtpendingattrs & TXTITALIC)
+ tsetcap(TCITALICSBEG, flags);
+
+ if (change & TXT_ATTR_FG_MASK)
+ set_colour_attribute(txtpendingattrs, COL_SEQ_FG, flags);
+ if (change & TXT_ATTR_BG_MASK)
+ set_colour_attribute(txtpendingattrs, COL_SEQ_BG, flags);
+
+ txtcurrentattrs = txtpendingattrs;
+}
+
+/**/
+mod_export void
+cleartextattributes(int flags)
+{
+ treplaceattrs(0);
+ applytextattributes(flags);
+}
+
+/**/
+mod_export void
+treplaceattrs(zattr newattrs)
+{
+ if (newattrs == TXT_ERROR)
+ return;
+
+ if (txtunknownattrs) {
+ /* Set current attributes to the opposite of the new ones
+ * for any that are unknown so that applytextattributes()
+ * detects them as changed. */
+ txtcurrentattrs &= ~txtunknownattrs;
+ txtcurrentattrs |= txtunknownattrs & ~newattrs;
+ }
+
+ txtpendingattrs = newattrs;
+}
+
+/**/
+mod_export void
+tsetattrs(zattr newattrs)
+{
+ /* assume any unknown attributes that we're now setting were unset */
+ txtcurrentattrs &= ~(newattrs & txtunknownattrs);
+
+ txtpendingattrs |= newattrs & TXT_ATTR_ALL;
+ if (newattrs & TXTFGCOLOUR) {
+ txtpendingattrs &= ~TXT_ATTR_FG_MASK;
+ txtpendingattrs |= newattrs & TXT_ATTR_FG_MASK;
+ }
+ if (newattrs & TXTBGCOLOUR) {
+ txtpendingattrs &= ~TXT_ATTR_BG_MASK;
+ txtpendingattrs |= newattrs & TXT_ATTR_BG_MASK;
+ }
+}
+
+/**/
+mod_export void
+tunsetattrs(zattr newattrs)
+{
+ /* assume any unknown attributes that we're now unsetting were set */
+ txtcurrentattrs |= newattrs & txtunknownattrs;
+
+ txtpendingattrs &= ~(newattrs & TXT_ATTR_ALL);
+ if (newattrs & TXTFGCOLOUR)
+ txtpendingattrs &= ~TXT_ATTR_FG_MASK;
+ if (newattrs & TXTBGCOLOUR)
+ txtpendingattrs &= ~TXT_ATTR_BG_MASK;
+}
+
+/* Merge two attribute sets. In an case where attributes might conflict
+ * choose those from the first parameter. Foreground and background
+ * colours are taken together - less likely to end up with unreadable
+ * combinations. */
+
+/**/
+mod_export zattr
+mixattrs(zattr primary, zattr secondary)
+{
+ zattr result = secondary;
+ /* take colours from primary */
+ if (primary & (TXTFGCOLOUR|TXTBGCOLOUR))
+ result &= ~TXT_ATTR_COLOUR_MASK;
+ /* take font weight from primary */
+ if (primary & TXT_ATTR_FONT_WEIGHT)
+ result &= ~TXT_ATTR_FONT_WEIGHT;
+ return result | primary;
+}
/*****************************************************************************
* Utilities dealing with colour and other forms of highlighting.
@@ -1607,10 +1800,12 @@ struct highlight {
};
static const struct highlight highlights[] = {
- { "none", 0, TXT_ATTR_ON_MASK },
- { "bold", TXTBOLDFACE, 0 },
+ { "none", 0, TXT_ATTR_ALL },
+ { "bold", TXTBOLDFACE, TXTFAINT },
+ { "faint", TXTFAINT, TXTBOLDFACE },
{ "standout", TXTSTANDOUT, 0 },
{ "underline", TXTUNDERLINE, 0 },
+ { "italic", TXTITALIC, 0 },
{ NULL, 0, 0 }
};
@@ -1645,8 +1840,8 @@ match_named_colour(const char **teststrp)
* Match just the colour part of a highlight specification.
* If teststrp is NULL, use the already parsed numeric colour.
* Return the attributes to set in the attribute variable.
- * Return -1 for out of range. Does not check the character
- * following the colour specification.
+ * Return TXT_ERROR for out of range. Does not check the
+ * character following the colour specification.
*/
/**/
@@ -1666,7 +1861,7 @@ match_colour(const char **teststrp, int is_fg, int colour)
tc = TCBGCOLOUR;
}
if (teststrp) {
- if (**teststrp == '#' && isxdigit(STOUC((*teststrp)[1]))) {
+ if (**teststrp == '#' && isxdigit((unsigned char) (*teststrp)[1])) {
struct color_rgb color;
char *end;
zlong col = zstrtol(*teststrp+1, &end, 16);
@@ -1693,10 +1888,8 @@ match_colour(const char **teststrp, int is_fg, int colour)
}
} else if ((named = ialpha(**teststrp))) {
colour = match_named_colour(teststrp);
- if (colour == 8) {
- /* default */
- return is_fg ? TXTNOFGCOLOUR : TXTNOBGCOLOUR;
- }
+ if (colour == 8) /* default */
+ return 0;
if (colour < 0)
return TXT_ERROR;
}
@@ -1717,23 +1910,30 @@ match_colour(const char **teststrp, int is_fg, int colour)
/*
* Match a set of highlights in the given teststr.
* Set *on_var to reflect the values found.
+ * Set *layer to the layer
* Return a pointer to the first character not consumed.
*/
/**/
mod_export const char *
-match_highlight(const char *teststr, zattr *on_var)
+match_highlight(const char *teststr, zattr *on_var, int *layer)
{
int found = 1;
*on_var = 0;
while (found && *teststr) {
const struct highlight *hl;
+ zattr atr = 0;
found = 0;
- if (strpfx("fg=", teststr) || strpfx("bg=", teststr)) {
+ if (strpfx("hl=", teststr)) {
+ teststr += 3;
+ teststr = parsehighlight((char *)teststr, ',', &atr);
+ if (atr != TXT_ERROR)
+ *on_var = atr;
+ found = 1;
+ } else if (strpfx("fg=", teststr) || strpfx("bg=", teststr)) {
int is_fg = (teststr[0] == 'f');
- zattr atr;
teststr += 3;
atr = match_colour(&teststr, is_fg, 0);
@@ -1745,6 +1945,14 @@ match_highlight(const char *teststr, zattr *on_var)
/* skip out of range colours but keep scanning attributes */
if (atr != TXT_ERROR)
*on_var |= atr;
+ } else if (layer && strpfx("layer=", teststr)) {
+ teststr += 6;
+ *layer = (int) zstrtol(teststr, (char **) &teststr, 10);
+ if (*teststr == ',')
+ teststr++;
+ else if (*teststr && *teststr != ' ')
+ break;
+ found = 1;
} else {
for (hl = highlights; hl->name; hl++) {
if (strpfx(hl->name, teststr)) {
@@ -1776,7 +1984,7 @@ match_highlight(const char *teststr, zattr *on_var)
static int
output_colour(int colour, int fg_bg, int truecol, char *buf)
{
- int atrlen = 3, len;
+ int atrlen = 3;
char *ptr = buf;
if (buf) {
strcpy(ptr, fg_bg == COL_SEQ_FG ? "fg=" : "bg=");
@@ -1794,14 +2002,11 @@ output_colour(int colour, int fg_bg, int truecol, char *buf)
*/
} else if (colour > 7) {
char digbuf[DIGBUFSIZE];
- sprintf(digbuf, "%d", colour);
- len = strlen(digbuf);
- atrlen += len;
+ atrlen += sprintf(digbuf, "%d", colour);
if (buf)
strcpy(ptr, digbuf);
} else {
- len = strlen(ansi_colours[colour]);
- atrlen += len;
+ atrlen += strlen(ansi_colours[colour]);
if (buf)
strcpy(ptr, ansi_colours[colour]);
}
@@ -2024,13 +2229,13 @@ set_colour_attribute(zattr atr, int fg_bg, int flags)
if (fg_bg == COL_SEQ_FG) {
colour = txtchangeget(atr, TXT_ATTR_FG_COL);
tc = TCFGCOLOUR;
- def = txtchangeisset(atr, TXTNOFGCOLOUR);
- use_truecolor = txtchangeisset(atr, TXT_ATTR_FG_24BIT);
+ def = !(atr & TXTFGCOLOUR);
+ use_truecolor = atr & TXT_ATTR_FG_24BIT;
} else {
colour = txtchangeget(atr, TXT_ATTR_BG_COL);
tc = TCBGCOLOUR;
- def = txtchangeisset(atr, TXTNOBGCOLOUR);
- use_truecolor = txtchangeisset(atr, TXT_ATTR_BG_24BIT);
+ def = !(atr & TXTBGCOLOUR);
+ use_truecolor = atr & TXT_ATTR_BG_24BIT;
}
/* Test if current zle_highlight settings are customized, or
diff --git a/Src/prototypes.h b/Src/prototypes.h
index e3db4f5ee..3578482d0 100644
--- a/Src/prototypes.h
+++ b/Src/prototypes.h
@@ -28,9 +28,9 @@
*/
#ifndef HAVE_STDLIB_H
-char *malloc _((size_t));
-char *realloc _((void *, size_t));
-char *calloc _((size_t, size_t));
+char *malloc (size_t);
+char *realloc (void *, size_t);
+char *calloc (size_t, size_t);
#endif
#if !(defined(USES_TERMCAP_H) || defined(USES_TERM_H))
@@ -45,11 +45,11 @@ char *calloc _((size_t, size_t));
#else
#define TC_CONST
#endif
-extern int tgetent _((char *bp, TC_CONST char *name));
-extern int tgetnum _((char *id));
-extern int tgetflag _((char *id));
-extern char *tgetstr _((char *id, char **area));
-extern int tputs _((TC_CONST char *cp, int affcnt, int (*outc) (int)));
+extern int tgetent (char *bp, TC_CONST char *name);
+extern int tgetnum (char *id);
+extern int tgetflag (char *id);
+extern char *tgetstr (char *id, char **area);
+extern int tputs (TC_CONST char *cp, int affcnt, int (*outc) (int));
#undef TC_CONST
#endif
@@ -70,30 +70,30 @@ char *tgoto(const char *cap, int col, int row);
#endif
#ifdef __osf__
-char *mktemp _((char *));
+char *mktemp (char *);
#endif
#if defined(__osf__) && defined(__alpha) && defined(__GNUC__)
/* Digital cc does not need these prototypes, gcc does need them */
# ifndef HAVE_IOCTL_PROTO
-int ioctl _((int d, unsigned long request, void *argp));
+int ioctl (int d, unsigned long request, void *argp);
# endif
# ifndef HAVE_MKNOD_PROTO
-int mknod _((const char *pathname, int mode, dev_t device));
+int mknod (const char *pathname, int mode, dev_t device);
# endif
-int nice _((int increment));
-int select _((int nfds, fd_set * readfds, fd_set * writefds, fd_set * exceptfds, struct timeval *timeout));
+int nice (int increment);
+int select (int nfds, fd_set * readfds, fd_set * writefds, fd_set * exceptfds, struct timeval *timeout);
#endif
#if defined(DGUX) && defined(__STDC__)
/* Just plain missing. */
-extern int getrlimit _((int resource, struct rlimit *rlp));
-extern int setrlimit _((int resource, const struct rlimit *rlp));
-extern int getrusage _((int who, struct rusage *rusage));
-extern int gettimeofday _((struct timeval *tv, struct timezone *tz));
-extern int wait3 _((union wait *wait_status, int options, struct rusage *rusage));
-extern int getdomainname _((char *name, int maxlength));
-extern int select _((int nfds, fd_set * readfds, fd_set * writefds, fd_set * exceptfds, struct timeval *timeout));
+extern int getrlimit (int resource, struct rlimit *rlp);
+extern int setrlimit (int resource, const struct rlimit *rlp);
+extern int getrusage (int who, struct rusage *rusage);
+extern int gettimeofday (struct timeval *tv, struct timezone *tz);
+extern int wait3 (union wait *wait_status, int options, struct rusage *rusage);
+extern int getdomainname (char *name, int maxlength);
+extern int select (int nfds, fd_set * readfds, fd_set * writefds, fd_set * exceptfds, struct timeval *timeout);
#endif /* DGUX and __STDC__ */
#ifdef __NeXT__
@@ -101,34 +101,34 @@ extern pid_t getppid(void);
#endif
#if defined(__sun__) && !defined(__SVR4) /* SunOS */
-extern char *strerror _((int errnum));
+extern char *strerror (int errnum);
#endif
/**************************************************/
/*** prototypes for functions built in compat.c ***/
#ifndef HAVE_STRSTR
-extern char *strstr _((const char *s, const char *t));
+extern char *strstr (const char *s, const char *t);
#endif
#ifndef HAVE_GETHOSTNAME
-extern int gethostname _((char *name, size_t namelen));
+extern int gethostname (char *name, size_t namelen);
#endif
#ifndef HAVE_GETTIMEOFDAY
-extern int gettimeofday _((struct timeval *tv, struct timezone *tz));
+extern int gettimeofday (struct timeval *tv, struct timezone *tz);
#endif
#ifndef HAVE_DIFFTIME
-extern double difftime _((time_t t2, time_t t1));
+extern double difftime (time_t t2, time_t t1);
#endif
#ifndef HAVE_STRERROR
-extern char *strerror _((int errnum));
+extern char *strerror (int errnum);
#endif
/*** end of prototypes for functions in compat.c ***/
/***************************************************/
#ifndef HAVE_MEMMOVE
-extern void bcopy _((const void *, void *, size_t));
+extern void bcopy (const void *, void *, size_t);
#endif
diff --git a/Src/signals.c b/Src/signals.c
index 5c787e2a8..de42f302d 100644
--- a/Src/signals.c
+++ b/Src/signals.c
@@ -31,10 +31,12 @@
#include "signals.pro"
/* Array describing the state of each signal: an element contains *
- * 0 for the default action or some ZSIG_* flags ored together. */
+ * 0 for the default action or some ZSIG_* flags ored together. *
+ * Contains TRAPCOUNT elements but can't be allocated statically *
+ * because that's a dynamic value on Linux */
/**/
-mod_export int sigtrapped[VSIGCOUNT];
+mod_export int *sigtrapped;
/*
* Trap programme lists for each signal.
@@ -48,7 +50,7 @@ mod_export int sigtrapped[VSIGCOUNT];
*/
/**/
-mod_export Eprog siglists[VSIGCOUNT];
+mod_export Eprog *siglists;
/* Total count of trapped signals */
@@ -89,33 +91,6 @@ mod_export volatile int trap_queueing_enabled, trap_queue_front, trap_queue_rear
/**/
mod_export int trap_queue[MAX_QUEUE_SIZE];
-/* This is only used on machines that don't understand signal sets. *
- * On SYSV machines this will represent the signals that are blocked *
- * (held) using sighold. On machines which can't block signals at *
- * all, we will simulate this by ignoring them and remembering them *
- * in this variable. */
-#if !defined(POSIX_SIGNALS) && !defined(BSD_SIGNALS)
-static sigset_t blocked_set;
-#endif
-
-#ifdef POSIX_SIGNALS
-# define signal_jmp_buf sigjmp_buf
-# define signal_setjmp(b) sigsetjmp((b),1)
-# define signal_longjmp(b,n) siglongjmp((b),(n))
-#else
-# define signal_jmp_buf jmp_buf
-# define signal_setjmp(b) setjmp(b)
-# define signal_longjmp(b,n) longjmp((b),(n))
-#endif
-
-#ifdef NO_SIGNAL_BLOCKING
-# define signal_process(sig) signal_ignore(sig)
-# define signal_reset(sig) install_handler(sig)
-#else
-# define signal_process(sig) ;
-# define signal_reset(sig) ;
-#endif
-
/* Install signal handler for given signal. *
* If possible, we want to make sure that interrupted *
* system calls are not restarted. */
@@ -124,10 +99,9 @@ static sigset_t blocked_set;
mod_export void
install_handler(int sig)
{
-#ifdef POSIX_SIGNALS
struct sigaction act;
- act.sa_handler = (SIGNAL_HANDTYPE) zhandler;
+ act.sa_handler = (void (*)(int)) zhandler;
sigemptyset(&act.sa_mask); /* only block sig while in handler */
act.sa_flags = 0;
# ifdef SA_INTERRUPT /* SunOS 4.x */
@@ -135,27 +109,6 @@ install_handler(int sig)
act.sa_flags |= SA_INTERRUPT; /* make sure system calls are not restarted */
# endif
sigaction(sig, &act, (struct sigaction *)NULL);
-#else
-# ifdef BSD_SIGNALS
- struct sigvec vec;
-
- vec.sv_handler = (SIGNAL_HANDTYPE) zhandler;
- vec.sv_mask = sigmask(sig); /* mask out this signal while in handler */
-# ifdef SV_INTERRUPT
- vec.sv_flags = SV_INTERRUPT; /* make sure system calls are not restarted */
-# endif
- sigvec(sig, &vec, (struct sigvec *)NULL);
-# else
-# ifdef SYSV_SIGNALS
- /* we want sigset rather than signal because it will *
- * block sig while in handler. signal usually doesn't */
- sigset(sig, zhandler);
-# else /* NO_SIGNAL_BLOCKING (bummer) */
- signal(sig, zhandler);
-
-# endif /* SYSV_SIGNALS */
-# endif /* BSD_SIGNALS */
-#endif /* POSIX_SIGNALS */
}
/* enable ^C interrupts */
@@ -218,49 +171,16 @@ signal_mask(int sig)
* set. Return the old signal set. */
/**/
-#ifndef BSD_SIGNALS
-
-/**/
mod_export sigset_t
signal_block(sigset_t set)
{
sigset_t oset;
-#ifdef POSIX_SIGNALS
sigprocmask(SIG_BLOCK, &set, &oset);
-#else
-# ifdef SYSV_SIGNALS
- int i;
-
- oset = blocked_set;
- for (i = 1; i <= NSIG; ++i) {
- if (sigismember(&set, i) && !sigismember(&blocked_set, i)) {
- sigaddset(&blocked_set, i);
- sighold(i);
- }
- }
-# else /* NO_SIGNAL_BLOCKING */
-/* We will just ignore signals if the system doesn't have *
- * the ability to block them. */
- int i;
-
- oset = blocked_set;
- for (i = 1; i <= NSIG; ++i) {
- if (sigismember(&set, i) && !sigismember(&blocked_set, i)) {
- sigaddset(&blocked_set, i);
- signal_ignore(i);
- }
- }
-# endif /* SYSV_SIGNALS */
-#endif /* POSIX_SIGNALS */
-
return oset;
}
-/**/
-#endif /* BSD_SIGNALS */
-
/* Unblock the signals in the given signal *
* set. Return the old signal set. */
@@ -270,39 +190,7 @@ signal_unblock(sigset_t set)
{
sigset_t oset;
-#ifdef POSIX_SIGNALS
sigprocmask(SIG_UNBLOCK, &set, &oset);
-#else
-# ifdef BSD_SIGNALS
- sigfillset(&oset);
- oset = sigsetmask(oset);
- sigsetmask(oset & ~set);
-# else
-# ifdef SYSV_SIGNALS
- int i;
-
- oset = blocked_set;
- for (i = 1; i <= NSIG; ++i) {
- if (sigismember(&set, i) && sigismember(&blocked_set, i)) {
- sigdelset(&blocked_set, i);
- sigrelse(i);
- }
- }
-# else /* NO_SIGNAL_BLOCKING */
-/* On systems that can't block signals, we are just ignoring them. So *
- * to unblock signals, we just reenable the signal handler for them. */
- int i;
-
- oset = blocked_set;
- for (i = 1; i <= NSIG; ++i) {
- if (sigismember(&set, i) && sigismember(&blocked_set, i)) {
- sigdelset(&blocked_set, i);
- install_handler(i);
- }
- }
-# endif /* SYSV_SIGNALS */
-# endif /* BSD_SIGNALS */
-#endif /* POSIX_SIGNALS */
return oset;
}
@@ -316,61 +204,18 @@ signal_setmask(sigset_t set)
{
sigset_t oset;
-#ifdef POSIX_SIGNALS
sigprocmask(SIG_SETMASK, &set, &oset);
-#else
-# ifdef BSD_SIGNALS
- oset = sigsetmask(set);
-# else
-# ifdef SYSV_SIGNALS
- int i;
-
- oset = blocked_set;
- for (i = 1; i <= NSIG; ++i) {
- if (sigismember(&set, i) && !sigismember(&blocked_set, i)) {
- sigaddset(&blocked_set, i);
- sighold(i);
- } else if (!sigismember(&set, i) && sigismember(&blocked_set, i)) {
- sigdelset(&blocked_set, i);
- sigrelse(i);
- }
- }
-# else /* NO_SIGNAL_BLOCKING */
- int i;
-
- oset = blocked_set;
- for (i = 1; i < NSIG; ++i) {
- if (sigismember(&set, i) && !sigismember(&blocked_set, i)) {
- sigaddset(&blocked_set, i);
- signal_ignore(i);
- } else if (!sigismember(&set, i) && sigismember(&blocked_set, i)) {
- sigdelset(&blocked_set, i);
- install_handler(i);
- }
- }
-# endif /* SYSV_SIGNALS */
-# endif /* BSD_SIGNALS */
-#endif /* POSIX_SIGNALS */
return oset;
}
-#if defined(NO_SIGNAL_BLOCKING)
-static int suspend_longjmp = 0;
-static signal_jmp_buf suspend_jmp_buf;
-#endif
-
/**/
int
signal_suspend(UNUSED(int sig), int wait_cmd)
{
int ret;
-#if defined(POSIX_SIGNALS) || defined(BSD_SIGNALS)
sigset_t set;
-# if defined(POSIX_SIGNALS) && defined(BROKEN_POSIX_SIGSUSPEND)
- sigset_t oset;
-# endif
sigemptyset(&set);
@@ -382,36 +227,8 @@ signal_suspend(UNUSED(int sig), int wait_cmd)
if (!(wait_cmd || isset(TRAPSASYNC) ||
(sigtrapped[SIGINT] & ~ZSIG_IGNORED)))
sigaddset(&set, SIGINT);
-#endif /* POSIX_SIGNALS || BSD_SIGNALS */
-#ifdef POSIX_SIGNALS
-# ifdef BROKEN_POSIX_SIGSUSPEND
- sigprocmask(SIG_SETMASK, &set, &oset);
- ret = pause();
- sigprocmask(SIG_SETMASK, &oset, NULL);
-# else /* not BROKEN_POSIX_SIGSUSPEND */
ret = sigsuspend(&set);
-# endif /* BROKEN_POSIX_SIGSUSPEND */
-#else /* not POSIX_SIGNALS */
-# ifdef BSD_SIGNALS
- ret = sigpause(set);
-# else
-# ifdef SYSV_SIGNALS
- ret = sigpause(sig);
-
-# else /* NO_SIGNAL_BLOCKING */
- /* need to use signal_longjmp to make this race-free *
- * between the child_unblock() and pause() */
- if (signal_setjmp(suspend_jmp_buf) == 0) {
- suspend_longjmp = 1; /* we want to signal_longjmp after catching signal */
- child_unblock(); /* do we need to do wait_cmd stuff as well? */
- ret = pause();
- }
- suspend_longjmp = 0; /* turn off using signal_longjmp since we are past *
- * the pause() function. */
-# endif /* SYSV_SIGNALS */
-# endif /* BSD_SIGNALS */
-#endif /* POSIX_SIGNALS */
return ret;
}
@@ -525,8 +342,7 @@ wait_for_processes(void)
zwarn("job can't be suspended");
} else {
#if defined(HAVE_WAIT3) && defined(HAVE_GETRUSAGE)
- struct timezone dummy_tz;
- gettimeofday(&pn->endtime, &dummy_tz);
+ zgettime_monotonic_if_available(&pn->endtime);
#ifdef WIFCONTINUED
if (WIFCONTINUED(status))
pn->status = SP_RUNNING;
@@ -556,9 +372,11 @@ wait_for_processes(void)
jn->gleader = 0;
}
}
+ update_bg_job(jn, pid, status);
update_job(jn);
} else if (findproc(pid, &jn, &pn, 1)) {
pn->status = status;
+ update_bg_job(jn, pid, status);
update_job(jn);
} else {
/* If not found, update the shell record of time spent by
@@ -567,21 +385,7 @@ wait_for_processes(void)
* terminates.
*/
get_usage();
- }
- /*
- * Accumulate a list of older jobs. We only do this for
- * background jobs, which is something in the job table
- * that's not marked as in the current shell or as shell builtin
- * and is not equal to the current foreground job.
- */
- if (jn && !(jn->stat & (STAT_CURSH|STAT_BUILTIN)) &&
- jn - jobtab != thisjob) {
- int val = (WIFSIGNALED(status) ?
- 0200 | WTERMSIG(status) :
- (WIFSTOPPED(status) ?
- 0200 | WEXITSTATUS(status) :
- WEXITSTATUS(status)));
- addbgstatus(pid, val);
+ update_bg_job(jn, pid, status);
}
unqueue_signals();
@@ -596,33 +400,12 @@ zhandler(int sig)
{
sigset_t newmask, oldmask;
-#if defined(NO_SIGNAL_BLOCKING)
- int do_jump;
- signal_jmp_buf jump_to;
-#endif
-
last_signal = sig;
- signal_process(sig);
sigfillset(&newmask);
/* Block all signals temporarily */
oldmask = signal_block(newmask);
-#if defined(NO_SIGNAL_BLOCKING)
- /* do we need to longjmp to signal_suspend */
- do_jump = suspend_longjmp;
- /* In case a SIGCHLD somehow arrives */
- suspend_longjmp = 0;
-
- /* Traps can cause nested signal_suspend() */
- if (sig == SIGCHLD) {
- if (do_jump) {
- /* Copy suspend_jmp_buf */
- jump_to = suspend_jmp_buf;
- }
- }
-#endif
-
/* Are we queueing signals now? */
if (queueing_enabled) {
int temp_rear = ++queue_rear % MAX_QUEUE_SIZE;
@@ -637,7 +420,6 @@ zhandler(int sig)
/* save current signal mask */
signal_mask_queue[queue_rear] = oldmask;
}
- signal_reset(sig);
return;
}
@@ -714,14 +496,6 @@ zhandler(int sig)
break;
} /* end of switch(sig) */
- signal_reset(sig);
-
-/* This is used to make signal_suspend() race-free */
-#if defined(NO_SIGNAL_BLOCKING)
- if (do_jump)
- signal_longjmp(jump_to, 1);
-#endif
-
} /* handler */
@@ -904,7 +678,7 @@ dosavetrap(int sig, int level)
* Set a trap: note this does not handle manipulation of
* the function table for TRAPNAL functions.
*
- * sig is the signal number.
+ * sig is index into the table of trapped signals.
*
* l is the list to be eval'd for a trap defined with the "trap"
* builtin and should be NULL for a function trap.
@@ -943,6 +717,10 @@ settrap(int sig, Eprog l, int flags)
#endif
sig != SIGCHLD)
signal_ignore(sig);
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+ else if (sig >= VSIGCOUNT && sig < TRAPCOUNT)
+ signal_ignore(SIGNUM(sig));
+#endif
} else {
nsigtrapped++;
sigtrapped[sig] = ZSIG_TRAPPED;
@@ -952,6 +730,10 @@ settrap(int sig, Eprog l, int flags)
#endif
sig != SIGCHLD)
install_handler(sig);
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+ if (sig >= VSIGCOUNT && sig < TRAPCOUNT)
+ install_handler(SIGNUM(sig));
+#endif
}
sigtrapped[sig] |= flags;
/*
@@ -1031,6 +813,11 @@ removetrap(int sig)
#endif
sig != SIGCHLD)
signal_default(sig);
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+ else if (sig >= VSIGCOUNT && sig < TRAPCOUNT)
+ signal_default(SIGNUM(sig));
+#endif
+
if (sig == SIGEXIT)
exit_trap_posix = 0;
@@ -1184,7 +971,7 @@ endtrapscope(void)
static int
handletrap(int sig)
{
- if (!sigtrapped[sig])
+ if (!sigtrapped[SIGIDX(sig)])
return 0;
if (trap_queueing_enabled)
@@ -1201,7 +988,7 @@ handletrap(int sig)
return 1;
}
- dotrap(sig);
+ dotrap(SIGIDX(sig));
if (sig == SIGALRM)
{
@@ -1493,3 +1280,60 @@ dotrap(int sig)
restore_queue_signals(q);
}
+
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+
+/* Realtime signals, these are a contiguous block that can
+ * be separated from the other signals with an unused gap. */
+
+/**/
+int
+rtsigno(const char* signame)
+{
+ const int maxofs = SIGRTMAX - SIGRTMIN;
+ const char *end = signame + 5;
+ int offset;
+ struct rtdir { int sig; int dir; char op; } x = { 0, 0, 0 };
+ if (!strncmp(signame, "RTMIN", 5)) {
+ x = (struct rtdir) { SIGRTMIN, 1, '+' };
+ } else if (!strncmp(signame, "RTMAX", 5)) {
+ x = (struct rtdir) { SIGRTMAX, -1, '-' };
+ } else
+ return 0;
+
+ if (signame[5] == x.op) {
+ if ((offset = strtol(signame + 6, (char **) &end, 10)) > maxofs)
+ return 0;
+ x.sig += offset * x.dir;
+ }
+ if (*end)
+ return 0;
+
+ return x.sig;
+}
+
+/**/
+char *
+rtsigname(int signo, int alt)
+{
+ char* buf = (char *) zhalloc(10);
+ int minofs = signo - SIGRTMIN;
+ int maxofs = SIGRTMAX - signo;
+ int offset;
+ int form = alt ^ (maxofs < minofs);
+
+ if (signo < SIGRTMIN || signo > SIGRTMAX)
+ return NULL;
+
+ strcpy(buf, "RT");
+ strcpy(buf+2, form ? "MAX-" : "MIN+");
+ offset = form ? maxofs : minofs;
+ if (offset) {
+ snprintf(buf + 6, 4, "%d", offset);
+ } else {
+ buf[5] = '\0';
+ }
+ return buf;
+}
+
+#endif
diff --git a/Src/signals.h b/Src/signals.h
index 41ac88cce..0540d4b0b 100644
--- a/Src/signals.h
+++ b/Src/signals.h
@@ -27,8 +27,6 @@
*
*/
-#define SIGNAL_HANDTYPE void (*)_((int))
-
#ifndef HAVE_KILLPG
# define killpg(pgrp,sig) kill(-(pgrp),sig)
#endif
@@ -36,26 +34,21 @@
#define SIGZERR (SIGCOUNT+1)
#define SIGDEBUG (SIGCOUNT+2)
#define VSIGCOUNT (SIGCOUNT+3)
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+# define TRAPCOUNT (VSIGCOUNT + SIGRTMAX - SIGRTMIN + 1)
+# define SIGNUM(x) ((x) >= VSIGCOUNT ? (x) - VSIGCOUNT + SIGRTMIN : (x))
+# define SIGIDX(x) ((x) >= SIGRTMIN && (x) <= SIGRTMAX ? (x) - SIGRTMIN + VSIGCOUNT : (x))
+#else
+# define TRAPCOUNT VSIGCOUNT
+# define SIGNUM(x) (x)
+# define SIGIDX(x) (x)
+#endif
#define SIGEXIT 0
#ifdef SV_BSDSIG
# define SV_INTERRUPT SV_BSDSIG
#endif
-/* If not a POSIX machine, then we create our *
- * own POSIX style signal sets functions. */
-#ifndef POSIX_SIGNALS
-# define sigemptyset(s) (*(s) = 0)
-# if NSIG == 32
-# define sigfillset(s) (*(s) = ~(sigset_t)0, 0)
-# else
-# define sigfillset(s) (*(s) = (1 << NSIG) - 1, 0)
-# endif
-# define sigaddset(s,n) (*(s) |= (1 << ((n) - 1)), 0)
-# define sigdelset(s,n) (*(s) &= ~(1 << ((n) - 1)), 0)
-# define sigismember(s,n) ((*(s) & (1 << ((n) - 1))) != 0)
-#endif /* ifndef POSIX_SIGNALS */
-
#define child_block() signal_block(sigchld_mask)
#define child_unblock() signal_unblock(sigchld_mask)
@@ -133,10 +126,5 @@
#define queue_signal_level() queueing_enabled
-#ifdef BSD_SIGNALS
-#define signal_block(S) sigblock(S)
-#else
-extern sigset_t signal_block _((sigset_t));
-#endif /* BSD_SIGNALS */
-
-extern sigset_t signal_unblock _((sigset_t));
+extern sigset_t signal_block (sigset_t);
+extern sigset_t signal_unblock (sigset_t);
diff --git a/Src/signames2.awk b/Src/signames2.awk
index 4d15681d5..0b254f751 100644
--- a/Src/signames2.awk
+++ b/Src/signames2.awk
@@ -13,8 +13,9 @@
signam = substr(tmp[1], 4, 20)
signum = tmp[2]
if (signam == "CHLD" && sig[signum] == "CLD") sig[signum] = ""
- if (signam == "POLL" && sig[signum] == "IO") sig[signum] = ""
- if (sig[signum] == "") {
+ if (signam == "POLL" && sig[signum] == "IO") sig[signum] = ""
+ if (signam == "ABRT" && sig[signum] == "IOT") sig[signum] = ""
+ if (signam !~ /RTM(IN|AX)/ && sig[signum] == "") {
sig[signum] = signam
if (0 + max < 0 + signum && signum < 60)
max = signum
@@ -26,6 +27,7 @@
if (signam == "CONT") { msg[signum] = "continued" }
if (signam == "EMT") { msg[signum] = "EMT instruction" }
if (signam == "FPE") { msg[signum] = "floating point exception" }
+ if (signam == "FREEZE") { msg[signum] = "checkpoint freeze" }
if (signam == "HUP") { msg[signum] = "hangup" }
if (signam == "ILL") { msg[signum] = "illegal hardware instruction" }
if (signam == "INFO") { msg[signum] = "status request from keyboard" }
@@ -33,23 +35,25 @@
if (signam == "IO") { msg[signum] = "i/o ready" }
if (signam == "IOT") { msg[signum] = "IOT instruction" }
if (signam == "KILL") { msg[signum] = "killed" }
- if (signam == "LOST") { msg[signum] = "resource lost" }
+ if (signam == "LOST") { msg[signum] = "resource lost" }
if (signam == "PIPE") { msg[signum] = "broken pipe" }
- if (signam == "POLL") { msg[signum] = "pollable event occurred" }
+ if (signam == "POLL") { msg[signum] = "pollable event occurred" }
if (signam == "PROF") { msg[signum] = "profile signal" }
if (signam == "PWR") { msg[signum] = "power fail" }
if (signam == "QUIT") { msg[signum] = "quit" }
if (signam == "SEGV") { msg[signum] = "segmentation fault" }
if (signam == "SYS") { msg[signum] = "invalid system call" }
if (signam == "TERM") { msg[signum] = "terminated" }
+ if (signam == "THAW") { msg[signum] = "checkpoint thaw" }
if (signam == "TRAP") { msg[signum] = "trace trap" }
- if (signam == "URG") { msg[signum] = "urgent condition" }
+ if (signam == "URG") { msg[signum] = "urgent condition" }
if (signam == "USR1") { msg[signum] = "user-defined signal 1" }
if (signam == "USR2") { msg[signum] = "user-defined signal 2" }
if (signam == "VTALRM") { msg[signum] = "virtual time alarm" }
if (signam == "WINCH") { msg[signum] = "window size changed" }
if (signam == "XCPU") { msg[signum] = "cpu limit exceeded" }
if (signam == "XFSZ") { msg[signum] = "file size limit exceeded" }
+ if (signam == "XRES") { msg[signum] = "resource control exceeded" }
}
}
@@ -65,10 +69,6 @@ END {
printf "#include %czsh.mdh%c\n", 34, 34
printf "\n"
printf "/**/\n"
- printf "#define sigmsg(sig) ((sig) <= SIGCOUNT ? sig_msg[sig]"
- printf " : %c%s%c)", 34, "unknown signal", 34
- printf "\n"
- printf "/**/\n"
printf "mod_export char *sig_msg[SIGCOUNT+2] = {\n"
printf "\t%c%s%c,\n", 34, "done", 34
diff --git a/Src/sort.c b/Src/sort.c
index 26949ad9c..ce2b4bbc3 100644
--- a/Src/sort.c
+++ b/Src/sort.c
@@ -138,7 +138,7 @@ eltpcmp(const void *a, const void *b)
int mul = 0;
for (; *as == *bs && *as; as++, bs++);
#ifndef HAVE_STRCOLL
- cmp = (int)STOUC(*as) - (int)STOUC(*bs);
+ cmp = (int) (unsigned char) *as - (int) (unsigned char) *bs;
#endif
if (sortnumeric < 0) {
if (*as == '-' && idigit(as[1]) && idigit(*bs)) {
@@ -159,7 +159,7 @@ eltpcmp(const void *a, const void *b)
bs++;
for (; idigit(*as) && *as == *bs; as++, bs++);
if (idigit(*as) || idigit(*bs)) {
- cmp = mul * ((int)STOUC(*as) - (int)STOUC(*bs));
+ cmp = mul * ((int) (unsigned char) *as - (int) (unsigned char) *bs);
while (idigit(*as) && idigit(*bs))
as++, bs++;
if (idigit(*as) && !idigit(*bs))
diff --git a/Src/string.c b/Src/string.c
index 5f439926e..0fa13ac0d 100644
--- a/Src/string.c
+++ b/Src/string.c
@@ -57,21 +57,6 @@ dupstring_wlen(const char *s, unsigned len)
return t;
}
-/* Duplicate string on heap, returning length of string */
-
-/**/
-mod_export char *
-dupstring_glen(const char *s, unsigned *len_ret)
-{
- char *t;
-
- if (!s)
- return NULL;
- t = (char *) zhalloc((*len_ret = strlen((char *)s)) + 1);
- strcpy(t, s);
- return t;
-}
-
/**/
mod_export char *
ztrdup(const char *s)
diff --git a/Src/subst.c b/Src/subst.c
index 0f98e6ea3..a079672df 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -556,7 +556,7 @@ multsub(char **s, int pf_flags, char ***a, int *isarr, char *sep,
for ( ; *x; x += l) {
char c = (l = *x == Meta) ? x[1] ^ 32 : *x;
l++;
- if (!iwsep(STOUC(c)))
+ if (!iwsep((unsigned char) c))
break;
*ms_flags |= MULTSUB_WS_AT_START;
}
@@ -573,7 +573,7 @@ multsub(char **s, int pf_flags, char ***a, int *isarr, char *sep,
convchar_t c;
if (*x == Dash)
*x = '-';
- if (itok(STOUC(*x))) {
+ if (itok((unsigned char) *x)) {
/* token, can't be separator, must be single byte */
rawc = *x;
l = 1;
@@ -582,7 +582,7 @@ multsub(char **s, int pf_flags, char ***a, int *isarr, char *sep,
if (!inq && !inp && WC_ZISTYPE(c, ISEP)) {
*x = '\0';
for (x += l; *x; x += l) {
- if (itok(STOUC(*x))) {
+ if (itok((unsigned char) *x)) {
/* as above */
rawc = *x;
l = 1;
@@ -1489,21 +1489,27 @@ subst_parse_str(char **sp, int single, int err)
static char *
substevalchar(char *ptr)
{
- zlong ires = mathevali(ptr);
+ zlong ires;
int len = 0;
+ int saved_errflag = errflag;
- if (errflag)
- return NULL;
-#ifdef MULTIBYTE_SUPPORT
- if (isset(MULTIBYTE) && ires > 127) {
- /* '\\' + 'U' + 8 bytes of character + '\0' */
- char buf[11];
+ errflag = 0;
+ ires = mathevali(ptr);
- /* inefficient: should separate out \U handling from getkeystring */
- sprintf(buf, "\\U%.8x", (unsigned int)ires & 0xFFFFFFFFu);
- ptr = getkeystring(buf, &len, GETKEYS_BINDKEY, NULL);
+ if (errflag) { /* not a valid numerical expression */
+ errflag |= saved_errflag;
+ return noerrs ? dupstring(""): NULL;
+ }
+ errflag |= saved_errflag;
+ if (ires < 0) {
+ zerr("character not in range");
+ }
+#ifdef MULTIBYTE_SUPPORT
+ else if (isset(MULTIBYTE) && ires > 127) {
+ ptr = zhalloc(MB_CUR_MAX+1);
+ len = ucs4tomb((unsigned int)ires & 0xffffffff, ptr);
}
- if (len == 0)
+ if (len <= 0)
#endif
{
ptr = zhalloc(2);
@@ -1818,14 +1824,14 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
* Use for the (k) flag. Goes down into the parameter code,
* sometimes.
*/
- char hkeys = 0;
+ int hkeys = 0;
/*
* Used for the (v) flag, ditto. Not quite sure why they're
* separate, but the tradition seems to be that things only
* get combined when that makes the result more obscure rather
* than less.
*/
- char hvals = 0;
+ int hvals = 0;
/*
* Whether we had to evaluate a subexpression, i.e. an
* internal ${...} or $(...) or plain $pm. We almost don't
@@ -1860,6 +1866,11 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
* joining the array into a string (for compatibility with ksh/bash).
*/
int quoted_array_with_offset = 0;
+ /*
+ * Nofork substitution controls
+ */
+ char *rplyvar = NULL; /* Indicates ${|...;} or ${{var} ...;} */
+ char *rplytmp = NULL; /* Indicates ${ ... ;} */
*s++ = '\0';
/*
@@ -1870,8 +1881,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
* these later on, too.
*/
c = *s;
- if (itype_end(s, IIDENT, 1) == s && *s != '#' && c != Pound &&
- !IS_DASH(c) &&
+ if (itype_end(s, (c == Inbrace ? INAMESPC : IIDENT), 1) == s &&
+ *s != '#' && c != Pound && !IS_DASH(c) &&
c != '!' && c != '$' && c != String && c != Qstring &&
c != '?' && c != Quest &&
c != '*' && c != Star && c != '@' && c != '{' &&
@@ -1887,19 +1898,235 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
* flags in parentheses, but also one ksh hack.
*/
if (c == Inbrace) {
- inbrace = 1;
- s++;
+ /* For processing nofork command substitution string */
+ char *cmdarg = NULL, *endvar = NULL, inchar = *++s;
+ char *outbracep = s, sav = *s;
+ Param rplypm = NULL;
+ size_t slen = 0;
+ int trim = (!EMULATION(EMULATE_ZSH)) ? 2 : !qt;
+
+ inbrace = 1; /* Outer scope boolean, see above */
+
+ /* Handling for nofork command substitution e.g. ${|cmd;}
+ * See other comments about kludges for why this is here.
+ *
+ * The command string is extracted and executed, and the
+ * substitution assigned. There's no (...)-flags processing,
+ * i.e. no ${|(U)cmd;}, because it looks quite awful and
+ * should not be part of command substitution in any case.
+ * Use ${(U)${|cmd;}} as you would for ${(U)$(cmd;)}.
+ */
+ if (inchar == '|' || inchar == Bar || inblank(inchar)) {
+ *s = Inbrace;
+ if (skipparens(Inbrace, Outbrace, &outbracep) == 0)
+ slen = outbracep - s - 1;
+ *s = sav;
+ if (inchar == '|')
+ inchar = Bar; /* Simplify later compares */
+ } else if (inchar == '{' || inchar == Inbrace) {
+ *s = Inbrace;
+ if ((outbracep = itype_end(s+1, INAMESPC, 0))) {
+ if (*outbracep == Inbrack &&
+ (outbracep = parse_subscript(++outbracep, 1, ']')))
+ ++outbracep;
+ }
+
+ /* If we reached the first close brace, find the last */
+ if (outbracep && *outbracep == Outbrace) {
+ char outchar = inchar == Inbrace ? Outbrace : '}';
+ endvar = outbracep++;
+
+ /* Require space to avoid ${{var}} typo for ${${var}} */
+ if (!inblank(*outbracep)) {
+ zerr("bad substitution");
+ return NULL;
+ }
+
+ *endvar = '|'; /* Almost anything but braces/brackets */
+ outbracep = s;
+ if (skipparens(Inbrace, outchar, &outbracep) == 0)
+ *endvar = Outbrace;
+ else { /* Never happens? */
+ *endvar = outchar;
+ outbracep = endvar + 1;
+ }
+ slen = outbracep - s - 1;
+ if (inchar != Inbrace)
+ outbracep[-1] = Outbrace;
+ *s = sav;
+ inchar = Inbrace; /* Simplify later compares */
+ } else {
+ zerr("bad substitution");
+ return NULL;
+ }
+ }
+ if (slen > 1) {
+ char *outbracep = s + slen;
+ if (!itok(*s) || inblank(inchar)) {
+ /* This tokenize() is important */
+ char sav = *outbracep;
+ *outbracep = '\0';
+ tokenize(s);
+ *outbracep = sav;
+ }
+ if (*outbracep == Outbrace) {
+ if (endvar == s+1) {
+ /* For consistency with ${} we allow ${{}...} */
+ rplyvar = NULL;
+ }
+ if (endvar && *endvar == Outbrace) {
+ cmdarg = dupstrpfx(endvar+1, outbracep-endvar-1);
+ rplyvar = dupstrpfx(s+1,endvar-s-1);
+ } else {
+ cmdarg = dupstrpfx(s+1, outbracep-s-1);
+ rplyvar = "REPLY";
+ }
+ if (inblank(inchar)) {
+ /*
+ * Admittedly a hack. Take advantage of the added
+ * parameter scope and the semantics of $(<file) to
+ * construct a command to write/read a temporary file.
+ * Then fall through to the regular parameter handling
+ * to manage word splitting, expansion flags, etc.
+ */
+ char *outfmt = ">| %s {\n%s\n;}"; /* 13 */
+ if ((rplytmp = gettempname(NULL, 1))) {
+ /* Prevent shenanigans with $TMPPREFIX */
+ char *tmpfile = quotestring(rplytmp, QT_BACKSLASH);
+ char *dummy = zhalloc(strlen(cmdarg) +
+ strlen(tmpfile) +
+ 13);
+ sprintf(dummy, outfmt, tmpfile, cmdarg);
+ cmdarg = dummy;
+ } else {
+ /* TMPPREFIX not writable? */
+ cmdoutval = lastval;
+ cmdarg = NULL;
+ }
+ }
+ s = outbracep;
+ }
+ }
+
+ if (rplyvar) {
+ /* char *rplyval = getsparam("REPLY"); cf. Future? below */
+ startparamscope(); /* "local" behaves as if in a function */
+ if (inchar == Bar) {
+ /* rplyvar should be REPLY at this point, but create
+ * hardwired name anyway to expose any bugs elsewhere
+ */
+ rplypm = createparam("REPLY", PM_LOCAL|PM_UNSET|PM_HIDE);
+ if (rplypm) /* Shouldn't createparam() do this? */
+ rplypm->level = locallevel;
+ /* Future? Expose global value of $REPLY if any? */
+ /* if (rplyval) setsparam("REPLY", ztrdup(rplyval)); */
+ } else if (inblank(inchar)) {
+ rplypm = createparam(".zsh.cmdsubst",
+ PM_LOCAL|PM_UNSET|PM_HIDE|
+ PM_READONLY_SPECIAL);
+ if (rplypm)
+ rplypm->level = locallevel;
+ }
+ if (inchar != Inbrace && !rplypm) {
+ zerr("failed to create scope for command substitution");
+ return NULL;
+ }
+ }
+
+ if (rplyvar && cmdarg && *cmdarg) {
+ int obreaks = breaks;
+ Eprog cmdprog;
+ /* Execute the shell command */
+ queue_signals();
+ untokenize(cmdarg);
+ cmdprog = parse_string(cmdarg, 0);
+ if (cmdprog) {
+ /* exec.c handles dont_queue_signals() */
+ execode(cmdprog, 1, 0, "cmdsubst");
+ cmdoutval = lastval;
+ /* "return" behaves as if in a function */
+ if (retflag) {
+ retflag = 0;
+ breaks = obreaks; /* Is this ever not zero? */
+ }
+ } else /* parse error */
+ errflag |= ERRFLAG_ERROR;
+ if (rplypm)
+ rplypm->node.flags &= ~PM_READONLY_SPECIAL;
+ if (rplytmp && !errflag) {
+ int onoerrs = noerrs, rplylen;
+ noerrs = 2;
+ rplylen = zstuff(&cmdarg, rplytmp);
+ if (trim) {
+ /* bash and ksh strip trailing newlines here */
+ while (rplylen > 0 && cmdarg[rplylen-1] == '\n') {
+ rplylen--;
+ if (trim == 1)
+ break;
+ }
+ cmdarg[rplylen] = 0;
+ }
+ noerrs = onoerrs;
+ if (rplylen >= 0)
+ setsparam(rplyvar, metafy(cmdarg, rplylen, META_REALLOC));
+ }
+ unqueue_signals();
+ }
+
+ if (rplytmp)
+ unlink(rplytmp);
+ if (rplyvar) {
+ if (inchar != Inbrace) {
+ if ((val = dupstring(getsparam(rplyvar))))
+ vunset = 0;
+ else {
+ vunset = 1;
+ val = dupstring("");
+ }
+ } else {
+ s = dyncat(rplyvar, s);
+ rplyvar = NULL;
+ }
+ endparamscope();
+ if (exit_pending) {
+ if (mypid == getpid()) {
+ /*
+ * paranoia: don't check for jobs, but there
+ * shouldn't be any if not interactive.
+ */
+ stopmsg = 1;
+ zexit(exit_val, ZEXIT_NORMAL);
+ } else
+ _exit(exit_val);
+ }
+ }
+
/*
* In ksh emulation a leading `!' is a special flag working
- * sort of like our (k).
+ * sort of like our (k). This is true only for arrays or
+ * associative arrays and only with subscripts [*] or [@],
+ * so zsh's implementation is approximate. For namerefs
+ * in ksh, ${!ref} substitues the parameter name at the
+ * end of any chain of references, rather than the value.
+ *
* TODO: this is one of very few cases tied directly to
* the emulation mode rather than an option. Since ksh
* doesn't have parameter flags it might be neater to
* handle this with the ^, =, ~ stuff, below.
*/
if ((c = *s) == '!' && s[1] != Outbrace && EMULATION(EMULATE_KSH)) {
- hkeys = SCANPM_WANTKEYS;
+ hkeys = SCANPM_WANTKEYS|SCANPM_NONAMEREF;
s++;
+ /* There's a slew of other special bash meanings of parameter
+ * references that start with "!":
+ * ${!name} == ${(P)name} (when name is not a nameref)
+ * ${!name*} == ${(k)parameters[(I)name*]}
+ * ${!name@} == ${(@k)parameters[(I)name*]}
+ * ${!name[*]} == ${(k)name} (but indexes of ordinary arrays, too)
+ * ${!name[@]} == ${(@k)name} (ditto, as noted above for ksh)
+ *
+ * See also workers/34390, workers/34397, workers/34408.
+ */
} else if (c == '(' || c == Inpar) {
char *t, sav;
int tt = 0;
@@ -2154,10 +2381,19 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
escapes = 1;
break;
+ case '!':
+ if ((hkeys|hvals) & ~SCANPM_NONAMEREF)
+ goto flagerr;
+ hkeys = SCANPM_NONAMEREF;
+ break;
case 'k':
+ if (hkeys & ~SCANPM_WANTKEYS)
+ goto flagerr;
hkeys = SCANPM_WANTKEYS;
break;
case 'v':
+ if (hvals & ~SCANPM_WANTVALS)
+ goto flagerr;
hvals = SCANPM_WANTVALS;
break;
@@ -2308,7 +2544,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
/*
* Look for special unparenthesised flags.
* TODO: could make these able to appear inside parentheses, too,
- * i.e. ${(^)...} etc.
+ * i.e. ${(^)...} etc., but ${(~)...} already has another meaning.
*/
for (;;) {
if ((c = *s) == '^' || c == Hat) {
@@ -2332,7 +2568,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
}
} else if ((c == '#' || c == Pound) &&
(inbrace || !isset(POSIXIDENTIFIERS)) &&
- (itype_end(s+1, IIDENT, 0) != s + 1
+ (itype_end(s+1, INAMESPC, 0) != s + 1
|| (cc = s[1]) == '*' || cc == Star || cc == '@'
|| cc == '?' || cc == Quest
|| cc == '$' || cc == String || cc == Qstring
@@ -2369,8 +2605,9 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
* Try to handle this when parameter is named
* by (P) (second part of test).
*/
- if (itype_end(s+1, IIDENT, 0) != s+1 || (aspar && isstring(s[1]) &&
- (s[2] == Inbrace || s[2] == Inpar)))
+ if (itype_end(s+1, INAMESPC, 0) != s+1 ||
+ (aspar && isstring(s[1]) &&
+ (s[2] == Inbrace || s[2] == Inpar)))
chkset = 1, s++;
else if (!inbrace) {
/* Special case for `$+' on its own --- leave unmodified */
@@ -2531,6 +2768,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
scanflags |= SCANPM_DQUOTED;
if (chkset)
scanflags |= SCANPM_CHECKING;
+ if (!inbrace)
+ scanflags |= SCANPM_NONAMESPC;
/*
* Second argument: decide whether to use the subexpression or
* the string next on the line as the parameter name.
@@ -2556,14 +2795,14 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
* we let fetchvalue set the main string pointer s to
* the end of the bit it's fetched.
*/
- if (!(v = fetchvalue(&vbuf, (subexp ? &ov : &s),
- (wantt ? -1 :
- ((unset(KSHARRAYS) || inbrace) ? 1 : -1)),
- scanflags)) ||
- (v->pm && (v->pm->node.flags & PM_UNSET)) ||
- (v->flags & VALFLAG_EMPTY))
+ if (!rplyvar &&
+ (!(v = fetchvalue(&vbuf, (subexp ? &ov : &s),
+ (wantt ? -1 :
+ ((unset(KSHARRAYS) || inbrace) ? 1 : -1)),
+ scanflags)) ||
+ (v->pm && (v->pm->node.flags & PM_UNSET)) ||
+ (v->flags & VALFLAG_EMPTY)))
vunset = 1;
-
if (wantt) {
/*
* Handle the (t) flag: value now becomes the type
@@ -2573,13 +2812,14 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
!(v->pm->node.flags & PM_UNSET))) {
int f = v->pm->node.flags;
- switch (PM_TYPE(f)) {
+ 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;
}
val = dupstring(val);
if (v->pm->level)
@@ -2910,6 +3150,13 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
chuck(ptr);
else
ptr++;
+ } else if (c == Dnull) {
+ chuck(ptr);
+ while (*ptr && *ptr != c)
+ ptr++;
+ if (*ptr == Dnull)
+ chuck(ptr);
+ ptr--; /* Outer loop is about to increment */
}
}
replstr = (*ptr && ptr[1]) ? ptr+1 : "";
@@ -2926,6 +3173,9 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
*/
if (!(flags & (SUB_MATCH|SUB_REST|SUB_BIND|SUB_EIND|SUB_LEN)))
flags |= SUB_REST;
+ /* If matching at start and end, don't stop early */
+ if ((flags & (SUB_START|SUB_END)) == (SUB_START|SUB_END))
+ flags |= SUB_LONG;
/*
* With ":" treat a value as unset if the variable is set but
@@ -3076,7 +3326,13 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
if (vunset) {
if (isset(EXECOPT)) {
*idend = '\0';
- zerr("%s: %s", idbeg, *s ? s : "parameter not set");
+ if (*s){
+ int l;
+ singsub(&s);
+ s = unmetafy(s, &l);
+ zerr("%s: %l", idbeg, s, l);
+ } else
+ zerr("%s: %s", idbeg, "parameter not set");
/*
* In interactive shell we need to return to
* top-level prompt --- don't clear this error
@@ -3203,7 +3459,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
shortest = 0;
++s;
}
- if (*itype_end(s, IIDENT, 0)) {
+ if (*itype_end(s, INAMESPC, 0)) {
untokenize(s);
zerr("not an identifier: %s", s);
return NULL;
@@ -3219,6 +3475,9 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
char *sval;
zip = getaparam(s);
if (!zip) {
+ zip = gethparam(s);
+ }
+ if (!zip) {
sval = getsparam(s);
if (sval)
zip = hmkarray(sval);
@@ -3263,7 +3522,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
int intersect = (*s == '*' || *s == Star);
char **compare, **ap, **apsrc;
++s;
- if (*itype_end(s, IIDENT, 0)) {
+ if (*itype_end(s, INAMESPC, 0)) {
untokenize(s);
zerr("not an identifier: %s", s);
return NULL;
@@ -3716,6 +3975,8 @@ colonsubscript:
if (presc) {
int ops = opts[PROMPTSUBST], opb = opts[PROMPTBANG];
int opp = opts[PROMPTPERCENT];
+ zattr savecurrent = txtcurrentattrs;
+ zattr saveunknown = txtunknownattrs;
if (presc < 2) {
opts[PROMPTPERCENT] = 1;
@@ -3738,7 +3999,8 @@ colonsubscript:
for (; *ap; ap++) {
char *tmps;
untokenize(*ap);
- tmps = promptexpand(*ap, 0, NULL, NULL, NULL);
+ txtunknownattrs = TXT_ATTR_ALL;
+ tmps = promptexpand(*ap, 0, NULL, NULL);
*ap = dupstring(tmps);
free(tmps);
}
@@ -3747,10 +4009,14 @@ colonsubscript:
if (!copied)
val = dupstring(val), copied = 1;
untokenize(val);
- tmps = promptexpand(val, 0, NULL, NULL, NULL);
+ txtunknownattrs = TXT_ATTR_ALL;
+ tmps = promptexpand(val, 0, NULL, NULL);
val = dupstring(tmps);
free(tmps);
}
+
+ txtpendingattrs = txtcurrentattrs = savecurrent;
+ txtunknownattrs = saveunknown;
opts[PROMPTSUBST] = ops;
opts[PROMPTBANG] = opb;
opts[PROMPTPERCENT] = opp;
@@ -4309,6 +4575,8 @@ modify(char **str, char **ptr, int inbrace)
break;
case 's':
+ case 'S':
+ hsubpatopt = (**ptr == 'S');
c = **ptr;
(*ptr)++;
ptr1 = *ptr;
@@ -4403,7 +4671,7 @@ modify(char **str, char **ptr, int inbrace)
break;
case '&':
- c = 's';
+ c = hsubpatopt ? 'S' : 's';
break;
case 'g':
@@ -4492,8 +4760,11 @@ modify(char **str, char **ptr, int inbrace)
copy = casemodify(tt, CASMOD_UPPER);
break;
case 's':
+ case 'S':
+ hsubpatopt = (c == 'S');
if (hsubl && hsubr)
- subst(&copy, hsubl, hsubr, gbal);
+ subst(&copy, dupstring(hsubl), dupstring(hsubr),
+ gbal, hsubpatopt);
break;
case 'q':
copy = quotestring(copy, QT_BACKSLASH_SHOWNULL);
@@ -4578,8 +4849,11 @@ modify(char **str, char **ptr, int inbrace)
*str = casemodify(*str, CASMOD_UPPER);
break;
case 's':
+ case 'S':
+ hsubpatopt = (c == 'S');
if (hsubl && hsubr)
- subst(str, hsubl, hsubr, gbal);
+ subst(str, dupstring(hsubl), dupstring(hsubr),
+ gbal, hsubpatopt);
break;
case 'q':
*str = quotestring(*str, QT_BACKSLASH);
diff --git a/Src/text.c b/Src/text.c
index 5cd7685fd..8b1bd96b6 100644
--- a/Src/text.c
+++ b/Src/text.c
@@ -335,7 +335,7 @@ getjobtext(Eprog prog, Wordcode c)
tlim = tptr + JOBTEXTSIZE - 1;
tjob = 1;
gettext2(&s);
- if (tptr[-1] == Meta)
+ if (tptr > jbuf && tptr[-1] == Meta)
--tptr;
*tptr = '\0';
freeeprog(prog); /* mark as unused */
@@ -578,11 +578,16 @@ gettext2(Estate state)
Wordcode end = p + WC_FUNCDEF_SKIP(code);
int nargs = *state->pc++;
+ if (nargs > 1)
+ taddstr("function ");
taddlist(state, nargs);
if (nargs)
taddstr(" ");
if (tjob) {
- taddstr("() { ... }");
+ if (nargs > 1)
+ taddstr("{ ... }");
+ else
+ taddstr("() { ... }");
state->pc = end;
if (!nargs) {
/*
@@ -594,7 +599,10 @@ gettext2(Estate state)
}
stack = 1;
} else {
- taddstr("() {");
+ if (nargs > 1)
+ taddstr("{");
+ else
+ taddstr("() {");
tindent++;
taddnl(1);
n = tpush(code, 1);
diff --git a/Src/utils.c b/Src/utils.c
index 62bd3e602..19fd61a8b 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -74,9 +74,6 @@ set_widearray(char *mb_array, Widechar_array wca)
}
wca->len = 0;
- if (!isset(MULTIBYTE))
- return;
-
if (mb_array) {
VARARR(wchar_t, tmpwcs, strlen(mb_array));
wchar_t *wcptr = tmpwcs;
@@ -86,9 +83,8 @@ set_widearray(char *mb_array, Widechar_array wca)
while (*mb_array) {
int mblen;
- if (STOUC(*mb_array) <= 0x7f) {
- mb_array++;
- *wcptr++ = (wchar_t)*mb_array;
+ if ((unsigned char) *mb_array <= 0x7f) {
+ *wcptr++ = (wchar_t)*mb_array++;
continue;
}
@@ -126,14 +122,20 @@ set_widearray(char *mb_array, Widechar_array wca)
(implemented by zerrmsg()):
Code Argument types Prints
- %s const char * C string (null terminated)
- %l const char *, int C string of given length (null not required)
+ %s const char * C string (metafied, null terminated)
+ (output "nice")
+ %l const char *, int C string of given length (not metafied)
+ (output raw)
%L long decimal value
%d int decimal value
%z zlong decimal value
%% (none) literal '%'
%c int character at that codepoint
- %e int strerror() message (argument is typically 'errno')
+ %e int strerror() message (argument usually 'errno')
+ (output raw)
+
+ For %s and %l, the caller is responsible for assuring end-of-string
+ is not in the middle of a metafy pair (%s) or a multibyte character.
*/
static void
@@ -314,14 +316,9 @@ zerrmsg(FILE *file, const char *fmt, va_list ap)
nicezputs(str, file);
break;
case 'l': {
- char *s;
str = va_arg(ap, const char *);
num = va_arg(ap, int);
- num = metalen(str, num);
- s = zhalloc(num + 1);
- memcpy(s, str, num);
- s[num] = '\0';
- nicezputs(s, file);
+ fwrite(str, num, 1, file);
break;
}
case 'L':
@@ -380,11 +377,13 @@ zerrmsg(FILE *file, const char *fmt, va_list ap)
fflush(file);
}
+#ifdef HAVE_SETUPTERM
/*
* Wrapper for setupterm() and del_curterm().
* These are called from terminfo.c and termcap.c.
*/
static int term_count; /* reference count of cur_term */
+#endif
/**/
mod_export void
@@ -717,7 +716,8 @@ wcs_nicechar(wchar_t c, size_t *widthp, char **swidep)
*/
/**/
-mod_export int is_wcs_nicechar(wchar_t c)
+mod_export int
+is_wcs_nicechar(wchar_t c)
{
if (!WC_ISPRINT(c) && (c < 0x80 || !isset(PRINTEIGHTBIT))) {
if (c == 0x7f || c == L'\n' || c == L'\t' || c < 0x20)
@@ -1071,7 +1071,7 @@ get_username(void)
cached_uid = current_uid;
zsfree(cached_username);
if ((pswd = getpwuid(current_uid)))
- cached_username = ztrdup(pswd->pw_name);
+ cached_username = ztrdup_metafy(pswd->pw_name);
else
cached_username = ztrdup("");
}
@@ -1543,7 +1543,8 @@ preprompt(void)
if (!eolmark)
eolmark = "%B%S%#%s%b";
opts[PROMPTPERCENT] = 1;
- str = promptexpand(eolmark, 1, NULL, NULL, NULL);
+ txtunknownattrs = TXT_ATTR_ALL;
+ str = promptexpand(eolmark, 1, NULL, NULL);
countprompt(str, &w, 0, -1);
opts[PROMPTPERCENT] = percents;
zputs(str, shout);
@@ -1569,14 +1570,14 @@ preprompt(void)
/* If 1) the parameter PERIOD exists, 2) a hook function for *
* "periodic" exists, 3) it's been greater than PERIOD since we *
* executed any such hook, then execute it now. */
- if (period && ((zlong)time(NULL) > (zlong)lastperiodic + period) &&
+ if (period && ((zlong)zmonotime(NULL) > (zlong)lastperiodic + period) &&
!callhookfunc("periodic", NULL, 1, NULL))
- lastperiodic = time(NULL);
+ lastperiodic = zmonotime(NULL);
if (errflag)
return;
/* Check mail */
- currentmailcheck = time(NULL);
+ currentmailcheck = zmonotime(NULL);
if (mailcheck &&
(zlong) difftime(currentmailcheck, lastmailcheck) > mailcheck) {
char *mailfile;
@@ -1713,7 +1714,7 @@ printprompt4(void)
opts[XTRACE] = 0;
unmetafy(s, &l);
s = unmetafy(promptexpand(metafy(s, l, META_NOALLOC),
- 0, NULL, NULL, NULL), &l);
+ 0, NULL, NULL), &l);
opts[XTRACE] = t;
fprintf(xtrerr, "%s", s);
@@ -1732,6 +1733,13 @@ freestr(void *a)
mod_export void
gettyinfo(struct ttyinfo *ti)
{
+ fdgettyinfo(SHTTY, ti);
+}
+
+/**/
+mod_export void
+fdgettyinfo(int SHTTY, struct ttyinfo *ti)
+{
if (SHTTY != -1) {
#ifdef HAVE_TERMIOS_H
# ifdef HAVE_TCGETATTR
@@ -1757,6 +1765,13 @@ gettyinfo(struct ttyinfo *ti)
mod_export void
settyinfo(struct ttyinfo *ti)
{
+ fdsettyinfo(SHTTY, ti);
+}
+
+/**/
+mod_export void
+fdsettyinfo(int SHTTY, struct ttyinfo *ti)
+{
if (SHTTY != -1) {
#ifdef HAVE_TERMIOS_H
# ifdef HAVE_TCGETATTR
@@ -1995,6 +2010,28 @@ redup(int x, int y)
{
int ret = y;
+#ifdef HAVE_FPURGE
+ /* Make sure buffers are cleared when changing descriptor for a
+ * FILE object. No fflush() here because the only way anything
+ * can legitimately be left in the buffer is when an error has
+ * occurred, so attempting flush here would at best error again
+ * and at worst squirt out something unexpected.
+ */
+ if (stdout && y == fileno(stdout))
+ fpurge(stdout);
+ if (stderr && y == fileno(stderr))
+ fpurge(stderr);
+ if (shout && y == fileno(shout))
+ fpurge(shout);
+ if (xtrerr && y == fileno(xtrerr))
+ fpurge(xtrerr);
+#ifndef _IONBF
+ /* See init.c setupshin() -- stdin otherwise unbuffered */
+ if (stdin && y == fileno(stdin))
+ fpurge(stdin);
+#endif
+#endif
+
if(x < 0)
zclose(y);
else if (x != y) {
@@ -2022,7 +2059,7 @@ 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:
+ * The most likely failures 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.
@@ -2724,6 +2761,19 @@ timespec_diff_us(const struct timespec *t1, const struct timespec *t2)
return (reverse ? LONG_MIN : LONG_MAX);
}
+/* Like time(), but uses the monotonic clock */
+
+/**/
+mod_export int
+zmonotime(time_t *tloc)
+{
+ struct timespec ts;
+ zgettime_monotonic_if_available(&ts);
+ if (tloc)
+ *tloc = ts.tv_sec;
+ return ts.tv_sec;
+}
+
/*
* Sleep for the given number of microseconds --- must be within
* range of a long at the moment, but this is only used for
@@ -2757,7 +2807,9 @@ zsleep(long us)
/**
* Sleep for time (fairly) randomly up to max_us microseconds.
- * Don't let the wallclock time extend beyond end_time.
+ * Don't let the time extend beyond end_time. end_time is compared to
+ * the current *monotonic* clock time, so do NOT base it on e.g. time(2);
+ * use zmonotime() or zgettime_monotonic_if_available().
* Return 1 if that seemed to work, else 0.
*
* For best results max_us should be a multiple of 2**16 or large
@@ -2769,7 +2821,7 @@ int
zsleep_random(long max_us, time_t end_time)
{
long r;
- time_t now = time(NULL);
+ time_t now = zmonotime(NULL);
/*
* Randomish backoff. Doesn't need to be fundamentally
@@ -2920,7 +2972,7 @@ read1char(int echo)
restore_queue_signals(q);
if (echo)
write_loop(SHTTY, &c, 1);
- return STOUC(c);
+ return (unsigned char) c;
}
/**/
@@ -3122,7 +3174,7 @@ spckword(char **s, int hist, int cmd, int ask)
if (**s == String && !*t) {
guess = *s + 1;
- if (itype_end(guess, IIDENT, 1) == guess)
+ if (itype_end(guess, INAMESPC, 1) == guess)
return;
ic = String;
d = 100;
@@ -3211,7 +3263,7 @@ spckword(char **s, int hist, int cmd, int ask)
x = 'n';
} else if (shout) {
char *pptbuf;
- pptbuf = promptexpand(sprompt, 0, best, guess, NULL);
+ pptbuf = promptexpand(sprompt, 0, best, guess);
zputs(pptbuf, shout);
free(pptbuf);
fflush(shout);
@@ -4118,27 +4170,41 @@ inittyptab(void)
* having IIDENT here is a good idea at all, but this code
* should disappear into history...
*/
- for (t0 = 0240; t0 != 0400; t0++)
- typtab[t0] = IALPHA | IALNUM | IIDENT | IUSER | IWORD;
+ if isset(MULTIBYTE)
+ for (t0 = 0240; t0 != 0400; t0++)
+ typtab[t0] = IALPHA | IALNUM | IIDENT | IUSER | IWORD;
#endif
/* typtab['.'] |= IIDENT; */ /* Allow '.' in variable names - broken */
typtab['_'] = IIDENT | IUSER;
- typtab['-'] = typtab['.'] = typtab[STOUC(Dash)] = IUSER;
+ typtab['-'] = typtab['.'] = typtab[(unsigned char) Dash] = IUSER;
typtab[' '] |= IBLANK | INBLANK;
typtab['\t'] |= IBLANK | INBLANK;
typtab['\n'] |= INBLANK;
typtab['\0'] |= IMETA;
- typtab[STOUC(Meta) ] |= IMETA;
- typtab[STOUC(Marker)] |= IMETA;
- for (t0 = (int)STOUC(Pound); t0 <= (int)STOUC(LAST_NORMAL_TOK); t0++)
+ typtab[(unsigned char) Meta ] |= IMETA;
+ typtab[(unsigned char) Marker] |= IMETA;
+ for (t0 = (int) (unsigned char) Pound; t0 <= (int) (unsigned char) LAST_NORMAL_TOK; t0++)
typtab[t0] |= ITOK | IMETA;
- for (t0 = (int)STOUC(Snull); t0 <= (int)STOUC(Nularg); t0++)
+ for (t0 = (int) (unsigned char) Snull; t0 <= (int) (unsigned char) Nularg; t0++)
typtab[t0] |= ITOK | IMETA | INULL;
- for (s = ifs ? ifs : EMULATION(EMULATE_KSH|EMULATE_SH) ?
- DEFAULT_IFS_SH : DEFAULT_IFS; *s; s++) {
- int c = STOUC(*s == Meta ? *++s ^ 32 : *s);
+ /* ifs */
+#define CURRENT_DEFAULT_IFS (EMULATION(EMULATE_KSH|EMULATE_SH) ? \
+ DEFAULT_IFS_SH : DEFAULT_IFS)
#ifdef MULTIBYTE_SUPPORT
- if (!isascii(c)) {
+ if (isset(MULTIBYTE)) {
+ set_widearray(ifs ? ifs : CURRENT_DEFAULT_IFS, &ifs_wide);
+ if (ifs && !ifs_wide.chars) {
+ zwarn("IFS has an invalid character; resetting IFS to default");
+ zsfree(ifs);
+ ifs = ztrdup(CURRENT_DEFAULT_IFS);
+ set_widearray(ifs, &ifs_wide);
+ }
+ }
+#endif
+ for (s = ifs ? ifs : CURRENT_DEFAULT_IFS; *s; s++) {
+ int c = (unsigned char) (*s == Meta ? *++s ^ 32 : *s);
+#ifdef MULTIBYTE_SUPPORT
+ if (isset(MULTIBYTE) && !isascii(c)) {
/* see comment for wordchars below */
continue;
}
@@ -4151,10 +4217,15 @@ inittyptab(void)
}
typtab[c] |= ISEP;
}
+ /* wordchars */
+#ifdef MULTIBYTE_SUPPORT
+ if (isset(MULTIBYTE))
+ set_widearray(wordchars, &wordchars_wide);
+#endif
for (s = wordchars ? wordchars : DEFAULT_WORDCHARS; *s; s++) {
- int c = STOUC(*s == Meta ? *++s ^ 32 : *s);
+ int c = (unsigned char) (*s == Meta ? *++s ^ 32 : *s);
#ifdef MULTIBYTE_SUPPORT
- if (!isascii(c)) {
+ if (isset(MULTIBYTE) && !isascii(c)) {
/*
* If we have support for multibyte characters, we don't
* handle non-ASCII characters here; instead, we turn
@@ -4167,22 +4238,17 @@ inittyptab(void)
#endif
typtab[c] |= IWORD;
}
-#ifdef MULTIBYTE_SUPPORT
- set_widearray(wordchars, &wordchars_wide);
- set_widearray(ifs ? ifs : EMULATION(EMULATE_KSH|EMULATE_SH) ?
- DEFAULT_IFS_SH : DEFAULT_IFS, &ifs_wide);
-#endif
for (s = SPECCHARS; *s; s++)
- typtab[STOUC(*s)] |= ISPECIAL;
+ typtab[(unsigned char) *s] |= ISPECIAL;
if (typtab_flags & ZTF_SP_COMMA)
- typtab[STOUC(',')] |= ISPECIAL;
+ typtab[(unsigned char) ','] |= ISPECIAL;
if (isset(BANGHIST) && bangchar && (typtab_flags & ZTF_INTERACT)) {
typtab_flags |= ZTF_BANGCHAR;
typtab[bangchar] |= ISPECIAL;
} else
typtab_flags &= ~ZTF_BANGCHAR;
for (s = PATCHARS; *s; s++)
- typtab[STOUC(*s)] |= IPATTERN;
+ typtab[(unsigned char) *s] |= IPATTERN;
unqueue_signals();
}
@@ -4193,10 +4259,10 @@ makecommaspecial(int yesno)
{
if (yesno != 0) {
typtab_flags |= ZTF_SP_COMMA;
- typtab[STOUC(',')] |= ISPECIAL;
+ typtab[(unsigned char) ','] |= ISPECIAL;
} else {
typtab_flags &= ~ZTF_SP_COMMA;
- typtab[STOUC(',')] &= ~ISPECIAL;
+ typtab[(unsigned char) ','] &= ~ISPECIAL;
}
}
@@ -4309,13 +4375,27 @@ wcsitype(wchar_t c, int itype)
* If "once" is set, just test the first character, i.e. (outptr !=
* inptr) tests whether the first character is valid in an identifier.
*
- * Currently this is only called with itype IIDENT, IUSER or ISEP.
+ * Currently called only with itype INAMESPC, IIDENT, IUSER or ISEP.
*/
/**/
mod_export char *
itype_end(const char *ptr, int itype, int once)
{
+ if (itype == INAMESPC) {
+ itype = IIDENT;
+ if (!isset(POSIXIDENTIFIERS) || EMULATION(EMULATE_KSH)) {
+ /* Special case for names containing ".", ksh93 namespaces */
+ char *t = itype_end(ptr + (*ptr == '.'), itype, 0);
+ if (t > ptr + (*ptr == '.')) {
+ if (*t == '.')
+ ptr = t + 1; /* Fall through */
+ else if (!once)
+ return t;
+ }
+ }
+ }
+
#ifdef MULTIBYTE_SUPPORT
if (isset(MULTIBYTE) &&
(itype != IIDENT || !isset(POSIXIDENTIFIERS))) {
@@ -4336,7 +4416,7 @@ itype_end(const char *ptr, int itype, int once)
if (wc == WEOF) {
/* invalid, treat as single character */
- int chr = STOUC(*ptr == Meta ? ptr[1] ^ 32 : *ptr);
+ int chr = (unsigned char) (*ptr == Meta ? ptr[1] ^ 32 : *ptr);
/* in this case non-ASCII characters can't match */
if (chr > 127 || !zistype(chr,itype))
break;
@@ -4375,7 +4455,7 @@ itype_end(const char *ptr, int itype, int once)
} else
#endif
for (;;) {
- int chr = STOUC(*ptr == Meta ? ptr[1] ^ 32 : *ptr);
+ int chr = (unsigned char) (*ptr == Meta ? ptr[1] ^ 32 : *ptr);
if (!zistype(chr,itype))
break;
ptr += (*ptr == Meta) ? 2 : 1;
@@ -4983,11 +5063,11 @@ unmeta_one(const char *in, int *sz)
*sz = mb_metacharlenconv_r(in, &wc, &wstate);
#else
if (in[0] == Meta) {
- *sz = 2;
- wc = STOUC(in[1] ^ 32);
+ *sz = 2;
+ wc = (unsigned char) (in[1] ^ 32);
} else {
- *sz = 1;
- wc = STOUC(in[0]);
+ *sz = 1;
+ wc = (unsigned char) in[0];
}
#endif
return wc;
@@ -5022,11 +5102,11 @@ ztrcmp(char const *s1, char const *s2)
if(!(c1 = *s1))
c1 = -1;
- else if(c1 == STOUC(Meta))
+ else if(c1 == (unsigned char) Meta)
c1 = *++s1 ^ 32;
if(!(c2 = *s2))
c2 = -1;
- else if(c2 == STOUC(Meta))
+ else if(c2 == (unsigned char) Meta)
c2 = *++s2 ^ 32;
if(c1 == c2)
@@ -5212,6 +5292,7 @@ nicedupstring(char const *s)
}
+/**/
#ifndef MULTIBYTE_SUPPORT
/* Unmetafy and output a string, displaying special characters readably. */
@@ -5246,8 +5327,9 @@ niceztrlen(char const *s)
}
return l;
}
-#endif
+/**/
+#endif
/**/
#ifdef MULTIBYTE_SUPPORT
@@ -5458,7 +5540,7 @@ mb_metacharlenconv_r(const char *s, wint_t *wcp, mbstate_t *mbsp)
const char *ptr;
wchar_t wc;
- if (STOUC(*s) <= 0x7f) {
+ if ((unsigned char) *s <= 0x7f) {
if (wcp)
*wcp = (wint_t)*s;
return 1;
@@ -5516,10 +5598,10 @@ mb_metacharlenconv_r(const char *s, wint_t *wcp, mbstate_t *mbsp)
mod_export int
mb_metacharlenconv(const char *s, wint_t *wcp)
{
- if (!isset(MULTIBYTE) || STOUC(*s) <= 0x7f) {
+ if (!isset(MULTIBYTE) || (unsigned char) *s <= 0x7f) {
/* treat as single byte, possibly metafied */
if (wcp)
- *wcp = (wint_t)(*s == Meta ? s[1] ^ 32 : *s);
+ *wcp = (wint_t)(unsigned char) (*s == Meta ? s[1] ^ 32 : *s);
return 1 + (*s == Meta);
}
/*
@@ -5581,7 +5663,7 @@ mb_metastrlenend(char *ptr, int width, char *eptr)
inchar = *ptr;
ptr++;
- if (complete && STOUC(inchar) <= STOUC(0x7f)) {
+ if (complete && (unsigned char) inchar <= (unsigned char) 0x7f) {
/*
* We rely on 7-bit US-ASCII as a subset, so skip
* multibyte handling if we have such a character.
@@ -5657,7 +5739,7 @@ mb_charlenconv_r(const char *s, int slen, wint_t *wcp, mbstate_t *mbsp)
const char *ptr;
wchar_t wc;
- if (slen && STOUC(*s) <= 0x7f) {
+ if (slen && (unsigned char) *s <= 0x7f) {
if (wcp)
*wcp = (wint_t)*s;
return 1;
@@ -5698,7 +5780,7 @@ mb_charlenconv_r(const char *s, int slen, wint_t *wcp, mbstate_t *mbsp)
mod_export int
mb_charlenconv(const char *s, int slen, wint_t *wcp)
{
- if (!isset(MULTIBYTE) || STOUC(*s) <= 0x7f) {
+ if (!isset(MULTIBYTE) || (unsigned char) *s <= 0x7f) {
if (wcp)
*wcp = (wint_t)*s;
return 1;
@@ -5717,7 +5799,7 @@ mod_export int
metacharlenconv(const char *x, int *c)
{
/*
- * Here we don't use STOUC() on the chars since they
+ * Here we don't use an (unsigned char) cast on the chars since they
* may be compared against other chars and this will fail
* if chars are signed and the high bit is set.
*/
@@ -5779,7 +5861,7 @@ sb_niceformat(const char *s, FILE *stream, char **outstrp, int flags)
eptr = ptr + umlen;
while (ptr < eptr) {
- int c = STOUC(*ptr);
+ int c = (unsigned char) *ptr;
if (c == '\'' && (flags & NICEFLAG_QUOTE)) {
fmt = "\\'";
newl = 2;
@@ -5996,9 +6078,9 @@ addunprintable(char *v, const char *u, const char *uend)
*/
int c;
if (*u == Meta)
- c = STOUC(*++u ^ 32);
+ c = (unsigned char) (*++u ^ 32);
else
- c = STOUC(*u);
+ c = (unsigned char) *u;
switch (c) {
case '\0':
*v++ = '\\';
@@ -6645,11 +6727,14 @@ dquotedzputs(char const *s, FILE *stream)
# if defined(HAVE_NL_LANGINFO) && defined(CODESET) && !defined(__STDC_ISO_10646__)
/* Convert a character from UCS4 encoding to UTF-8 */
-static size_t
+static int
ucs4toutf8(char *dest, unsigned int wval)
{
- size_t len;
+ int len;
+ /* UCS4 is now equvalent to UTF-32 and limited to 0 - 0x10_FFFF.
+ * This function accepts 0 - 0x7FFF_FFFF (old range of UCS4) to be
+ * compatible with wctomb(3) (in UTF-8 locale) on Linux. */
if (wval < 0x80)
len = 1;
else if (wval < 0x800)
@@ -6660,8 +6745,12 @@ ucs4toutf8(char *dest, unsigned int wval)
len = 4;
else if (wval < 0x4000000)
len = 5;
- else
+ else if (wval < 0x80000000)
len = 6;
+ else {
+ zerr("character not in range");
+ return -1;
+ }
switch (len) { /* falls through except to the last case */
case 6: dest[5] = (wval & 0x3f) | 0x80; wval >>= 6;
@@ -6678,30 +6767,89 @@ ucs4toutf8(char *dest, unsigned int wval)
}
#endif
+/* Convert UCS4 to a multibyte character in current locale.
+ * Result is saved in buf (must be at least MB_CUR_MAX bytes long).
+ * Returns the number of bytes saved in buf, or -1 if conversion fails. */
-/*
- * The following only occurs once or twice in the code, but in different
- * places depending how character set conversion is implemented.
- */
-#define CHARSET_FAILED() \
- if (how & GETKEY_DOLLAR_QUOTE) { \
- while ((*tdest++ = *++s)) { \
- if (how & GETKEY_UPDATE_OFFSET) { \
- if (s - sstart > *misc) \
- (*misc)++; \
- } \
- if (*s == Snull) { \
- *len = (s - sstart) + 1; \
- *tdest = '\0'; \
- return buf; \
- } \
- } \
- *len = tdest - buf; \
- return buf; \
- } \
- *t = '\0'; \
- *len = t - buf; \
- return buf
+/**/
+int
+ucs4tomb(unsigned int wval, char *buf)
+{
+#if defined(HAVE_WCHAR_H) && defined(HAVE_WCTOMB) && defined(__STDC_ISO_10646__)
+ int count = wctomb(buf, (wchar_t)wval);
+ if (count == -1)
+ zerr("character not in range");
+ return count;
+#else /* !(HAVE_WCHAR_H && HAVE_WCTOMB && __STDC_ISO_10646__) */
+# if defined(HAVE_NL_LANGINFO) && defined(CODESET)
+ if (!strcmp(nl_langinfo(CODESET), "UTF-8")) {
+ return ucs4toutf8(buf, wval);
+ } else {
+# ifdef HAVE_ICONV
+ iconv_t cd;
+ char inbuf[4], *bsave = buf;
+ ICONV_CONST char *inptr = inbuf;
+ size_t inbytes = 4, outbytes = 6;
+ const char *codesetstr = nl_langinfo(CODESET);
+ size_t count;
+ int i;
+
+ /*
+ * If the code set isn't handled, we'd better assume it's US-ASCII
+ * rather than just failing hopelessly. Solaris has a weird habit
+ * of returning 646. This is handled by the native iconv(), but
+ * not by GNU iconv; what's more, some versions of the native iconv
+ * don't handle standard names like ASCII.
+ *
+ * This should only be a problem if there's a mismatch between the
+ * NLS and the iconv in use, which probably only means if libiconv
+ * is in use. We checked at configure time if our libraries pulled
+ * in _libiconv_version, which should be a good test.
+ *
+ * It shouldn't ever be NULL, but while we're being paranoid...
+ */
+# ifdef ICONV_FROM_LIBICONV
+ if (!codesetstr || !*codesetstr)
+ codesetstr = "US-ASCII";
+# endif
+ cd = iconv_open(codesetstr, "UCS-4BE");
+# ifdef ICONV_FROM_LIBICONV
+ if (cd == (iconv_t)-1 && !strcmp(codesetstr, "646")) {
+ codesetstr = "US-ASCII";
+ cd = iconv_open(codesetstr, "UCS-4BE");
+ }
+# endif
+ if (cd == (iconv_t)-1) {
+ zerr("cannot do charset conversion (iconv failed)");
+ return -1;
+ }
+
+ /* store value in big endian form */
+ for (i=3; i>=0; i--) {
+ inbuf[i] = wval & 0xff;
+ wval >>= 8;
+ }
+ count = iconv(cd, &inptr, &inbytes, &buf, &outbytes);
+ iconv_close(cd);
+ if (count) {
+ /* -1 indicates error. Positive value means number of "invalid"
+ * (or "non-reversible") conversions, which we consider as
+ * "out-of-range" characters. */
+ zerr("character not in range");
+ return -1;
+ }
+ return buf - bsave;
+# else /* !HAVE_ICONV */
+ zerr("cannot do charset conversion (iconv not available)");
+ return -1;
+# endif /* HAVE_ICONV */
+ }
+# else /* !(HAVE_NL_LANGINFO && CODESET) */
+ zerr("cannot do charset conversion (NLS not supported)");
+ return -1;
+# endif /* HAVE_NL_LANGINFO && CODESET */
+#endif /* HAVE_WCHAR_H && HAVE_WCTOMB && __STDC_ISO_10646__ */
+}
/*
* Decode a key string, turning it into the literal characters.
@@ -6758,21 +6906,6 @@ getkeystring(char *s, int *len, int how, int *misc)
char *t, *tdest = NULL, *u = NULL, *sstart = s, *tbuf = NULL;
char svchar = '\0';
int meta = 0, control = 0, ignoring = 0;
- int i;
-#if defined(HAVE_WCHAR_H) && defined(HAVE_WCTOMB) && defined(__STDC_ISO_10646__)
- wint_t wval;
- int count;
-#else
- unsigned int wval;
-# if defined(HAVE_NL_LANGINFO) && defined(CODESET)
-# if defined(HAVE_ICONV)
- iconv_t cd;
- char inbuf[4];
- size_t inbytes, outbytes;
-# endif
- size_t count;
-# endif
-#endif
DPUTS((how & GETKEY_UPDATE_OFFSET) &&
(how & ~(GETKEYS_DOLLARS_QUOTE|GETKEY_UPDATE_OFFSET)),
@@ -6837,7 +6970,8 @@ getkeystring(char *s, int *len, int how, int *misc)
}
for (; *s; s++) {
if (*s == '\\' && s[1]) {
- int miscadded;
+ int miscadded, count, i;
+ unsigned int wval;
if ((how & GETKEY_UPDATE_OFFSET) && s - sstart < *misc) {
(*misc)--;
miscadded = 1;
@@ -6952,86 +7086,32 @@ getkeystring(char *s, int *len, int how, int *misc)
*misc = wval;
return s+1;
}
-#if defined(HAVE_WCHAR_H) && defined(HAVE_WCTOMB) && defined(__STDC_ISO_10646__)
- count = wctomb(t, (wchar_t)wval);
+ count = ucs4tomb(wval, t);
if (count == -1) {
- zerr("character not in range");
- CHARSET_FAILED();
+ if (how & GETKEY_DOLLAR_QUOTE) {
+ while ((*tdest++ = *++s)) {
+ if (how & GETKEY_UPDATE_OFFSET) {
+ if (s - sstart > *misc)
+ (*misc)++;
+ }
+ if (*s == Snull) {
+ *len = (s - sstart) + 1;
+ *tdest = '\0';
+ return buf;
+ }
+ }
+ *len = tdest - buf;
+ }
+ else {
+ *t = '\0';
+ *len = t - buf;
+ }
+ return buf;
}
if ((how & GETKEY_UPDATE_OFFSET) && s - sstart < *misc)
(*misc) += count;
t += count;
-# else
-# if defined(HAVE_NL_LANGINFO) && defined(CODESET)
- if (!strcmp(nl_langinfo(CODESET), "UTF-8")) {
- count = ucs4toutf8(t, wval);
- t += count;
- if ((how & GETKEY_UPDATE_OFFSET) && s - sstart < *misc)
- (*misc) += count;
- } else {
-# ifdef HAVE_ICONV
- ICONV_CONST char *inptr = inbuf;
- const char *codesetstr = nl_langinfo(CODESET);
- inbytes = 4;
- outbytes = 6;
- /* store value in big endian form */
- for (i=3;i>=0;i--) {
- inbuf[i] = wval & 0xff;
- wval >>= 8;
- }
- /*
- * If the code set isn't handled, we'd better
- * assume it's US-ASCII rather than just failing
- * hopelessly. Solaris has a weird habit of
- * returning 646. This is handled by the
- * native iconv(), but not by GNU iconv; what's
- * more, some versions of the native iconv don't
- * handle standard names like ASCII.
- *
- * This should only be a problem if there's a
- * mismatch between the NLS and the iconv in use,
- * which probably only means if libiconv is in use.
- * We checked at configure time if our libraries
- * pulled in _libiconv_version, which should be
- * a good test.
- *
- * It shouldn't ever be NULL, but while we're
- * being paranoid...
- */
-#ifdef ICONV_FROM_LIBICONV
- if (!codesetstr || !*codesetstr)
- codesetstr = "US-ASCII";
-#endif
- cd = iconv_open(codesetstr, "UCS-4BE");
-#ifdef ICONV_FROM_LIBICONV
- if (cd == (iconv_t)-1 && !strcmp(codesetstr, "646")) {
- codesetstr = "US-ASCII";
- cd = iconv_open(codesetstr, "UCS-4BE");
- }
-#endif
- if (cd == (iconv_t)-1) {
- zerr("cannot do charset conversion (iconv failed)");
- CHARSET_FAILED();
- }
- count = iconv(cd, &inptr, &inbytes, &t, &outbytes);
- iconv_close(cd);
- if (count == (size_t)-1) {
- zerr("character not in range");
- CHARSET_FAILED();
- }
- if ((how & GETKEY_UPDATE_OFFSET) && s - sstart < *misc)
- (*misc) += count;
-# else
- zerr("cannot do charset conversion (iconv not available)");
- CHARSET_FAILED();
-# endif
- }
-# else
- zerr("cannot do charset conversion (NLS not supported)");
- CHARSET_FAILED();
-# endif
-# endif
if (how & GETKEY_DOLLAR_QUOTE) {
char *t2;
for (t2 = tbuf; t2 < t; t2++) {
@@ -7104,7 +7184,7 @@ getkeystring(char *s, int *len, int how, int *misc)
continue;
#ifdef MULTIBYTE_SUPPORT
} else if ((how & GETKEY_SINGLE_CHAR) &&
- isset(MULTIBYTE) && STOUC(*s) > 127) {
+ isset(MULTIBYTE) && (unsigned char) *s > 127) {
wint_t wc;
int len;
len = mb_metacharlenconv(s, &wc);
@@ -7207,7 +7287,7 @@ getkeystring(char *s, int *len, int how, int *misc)
t = tbuf;
}
if ((how & GETKEY_SINGLE_CHAR) && t != tmp) {
- *misc = STOUC(tmp[0]);
+ *misc = (unsigned char) tmp[0];
return s + 1;
}
}
@@ -7498,8 +7578,8 @@ restoredir(struct dirsav *d)
else if (d->level < 0)
err = -1;
if (d->dev || d->ino) {
- stat(".", &sbuf);
- if (sbuf.st_ino != d->ino || sbuf.st_dev != d->dev)
+ if (stat(".", &sbuf) < 0 ||
+ sbuf.st_ino != d->ino || sbuf.st_dev != d->dev)
err = -2;
}
return err;
@@ -7524,9 +7604,9 @@ privasserted(void)
/* POSIX doesn't define a way to test whether a capability set *
* is empty or not. Typical. I hope this is conforming... */
cap_flag_value_t val;
- cap_value_t n;
- for(n = 0; !cap_get_flag(caps, n, CAP_EFFECTIVE, &val); n++)
- if(val) {
+ cap_value_t cap;
+ for(cap = 0; !cap_get_flag(caps, cap, CAP_EFFECTIVE, &val); cap++)
+ if(val && cap != CAP_WAKE_ALARM) {
cap_free(caps);
return 1;
}
@@ -7570,6 +7650,7 @@ mode_to_octal(mode_t mode)
return m;
}
+/**/
#ifdef MAILDIR_SUPPORT
/*
* Stat a file. If it's a maildir, check all messages
@@ -7693,4 +7774,6 @@ mailstat(char *path, struct stat *st)
*st = st_ret_last = st_ret;
return 0;
}
+
+/**/
#endif
diff --git a/Src/zsh.h b/Src/zsh.h
index 40f9ea537..4e5c02980 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -135,19 +135,6 @@ struct mathfunc {
#define STRMATHFUNC(name, func, id) \
{ NULL, name, MFF_STR, NULL, func, NULL, 0, 0, id }
-/* Character tokens are sometimes casted to (unsigned char)'s. *
- * Unfortunately, some compilers don't correctly cast signed to *
- * unsigned promotions; i.e. (int)(unsigned char)((char) -1) evaluates *
- * to -1, instead of 255 like it should. We circumvent the troubles *
- * of such shameful delinquency by casting to a larger unsigned type *
- * then back down to unsigned char. */
-
-#ifdef BROKEN_SIGNED_TO_UNSIGNED_CASTING
-# define STOUC(X) ((unsigned char)(unsigned short)(X))
-#else
-# define STOUC(X) ((unsigned char)(X))
-#endif
-
/* Meta together with the character following Meta denotes the character *
* which is the exclusive or of 32 and the character following Meta. *
* This is used to represent characters which otherwise has special *
@@ -309,6 +296,9 @@ enum {
/*
* Lexical tokens: unlike the character tokens above, these never
* appear in strings and don't necessarily represent a single character.
+ *
+ * See Src/lex.c:tokstrings[] for hints on what these mean. Note that
+ * SEPER or SEMI are both stringified as ";".
*/
enum lextok {
@@ -628,7 +618,7 @@ union linkroot {
/* Specific elements of linked lists */
/*************************************/
-typedef void (*voidvoidfnptr_t) _((void));
+typedef void (*voidvoidfnptr_t) (void);
/*
* Element of the prepromptfns list.
@@ -688,7 +678,7 @@ struct timedfn {
#define COND_MOD 18
#define COND_MODI 19
-typedef int (*CondHandler) _((char **, int));
+typedef int (*CondHandler) (char **, int);
struct conddef {
Conddef next; /* next in list */
@@ -1125,8 +1115,8 @@ struct process {
char text[JOBTEXTSIZE]; /* text to print when 'jobs' is run */
int status; /* return code from waitpid/wait3() */
child_times_t ti;
- struct timeval bgtime; /* time job was spawned */
- struct timeval endtime; /* time job exited */
+ struct timespec bgtime; /* time job was spawned */
+ struct timespec endtime; /* time job exited */
};
struct execstack {
@@ -1174,28 +1164,28 @@ struct dirsav {
/* Definitions for Hash Tables */
/*******************************/
-typedef void *(*VFunc) _((void *));
-typedef void (*FreeFunc) _((void *));
+typedef void *(*VFunc) (void *);
+typedef void (*FreeFunc) (void *);
-typedef unsigned (*HashFunc) _((const char *));
-typedef void (*TableFunc) _((HashTable));
+typedef unsigned (*HashFunc) (const char *);
+typedef void (*TableFunc) (HashTable);
/*
* Note that this is deliberately "char *", not "const char *",
* since the AddNodeFunc is passed a pointer to a string that
* will be stored and later freed.
*/
-typedef void (*AddNodeFunc) _((HashTable, char *, void *));
-typedef HashNode (*GetNodeFunc) _((HashTable, const char *));
-typedef HashNode (*RemoveNodeFunc) _((HashTable, const char *));
-typedef void (*FreeNodeFunc) _((HashNode));
-typedef int (*CompareFunc) _((const char *, const char *));
+typedef void (*AddNodeFunc) (HashTable, char *, void *);
+typedef HashNode (*GetNodeFunc) (HashTable, const char *);
+typedef HashNode (*RemoveNodeFunc) (HashTable, const char *);
+typedef void (*FreeNodeFunc) (HashNode);
+typedef int (*CompareFunc) (const char *, const char *);
/* type of function that is passed to *
* scanhashtable or scanmatchtable */
-typedef void (*ScanFunc) _((HashNode, int));
-typedef void (*ScanTabFunc) _((HashTable, ScanFunc, int));
+typedef void (*ScanFunc) (HashNode, int);
+typedef void (*ScanTabFunc) (HashTable, ScanFunc, int);
-typedef void (*PrintTableStats) _((HashTable));
+typedef void (*PrintTableStats) (HashTable);
/* Hash table for standard open hashing. Instances of struct hashtable can be *
* created only by newhashtable(). In fact, this function creates an instance *
@@ -1362,7 +1352,7 @@ struct funcstack {
/* node in list of function call wrappers */
-typedef int (*WrapFunc) _((Eprog, FuncWrap, char *));
+typedef int (*WrapFunc) (Eprog, FuncWrap, char *);
struct funcwrap {
FuncWrap next;
@@ -1438,8 +1428,8 @@ enum {
* builtin structure.
*/
-typedef int (*HandlerFunc) _((char *, char **, Options, int));
-typedef int (*HandlerFuncAssign) _((char *, char **, LinkList, Options, int));
+typedef int (*HandlerFunc) (char *, char **, Options, int);
+typedef int (*HandlerFuncAssign) (char *, char **, LinkList, Options, int);
#define NULLBINCMD ((HandlerFunc) 0)
struct builtin {
@@ -1536,10 +1526,10 @@ struct module {
/* Module record is an alias */
#define MOD_ALIAS (1<<6)
-typedef int (*Module_generic_func) _((void));
-typedef int (*Module_void_func) _((Module));
-typedef int (*Module_features_func) _((Module, char ***));
-typedef int (*Module_enables_func) _((Module, int **));
+typedef int (*Module_generic_func) (void);
+typedef int (*Module_void_func) (Module);
+typedef int (*Module_features_func) (Module, char ***);
+typedef int (*Module_enables_func) (Module, int **);
struct linkedmod {
char *name;
@@ -1584,11 +1574,11 @@ struct feature_enables {
/* C-function hooks */
-typedef int (*Hookfn) _((Hookdef, void *));
+typedef int (*Hookfn) (Hookdef, void *);
struct hookdef {
Hookdef next;
- char *name;
+ const char *name;
Hookfn def;
int flags;
LinkList funcs;
@@ -1677,7 +1667,7 @@ enum zpc_chars {
ZPC_KSH_BANG, /* ! for !(...) in KSH_GLOB */
ZPC_KSH_BANG2, /* ! for !(...) in KSH_GLOB, untokenised */
ZPC_KSH_AT, /* @ for @(...) in KSH_GLOB */
- ZPC_COUNT /* Number of special chararacters */
+ ZPC_COUNT /* Number of special characters */
};
/*
@@ -1799,33 +1789,33 @@ typedef const struct gsu_array *GsuArray;
typedef const struct gsu_hash *GsuHash;
struct gsu_scalar {
- char *(*getfn) _((Param));
- void (*setfn) _((Param, char *));
- void (*unsetfn) _((Param, int));
+ char *(*getfn) (Param);
+ void (*setfn) (Param, char *);
+ void (*unsetfn) (Param, int);
};
struct gsu_integer {
- zlong (*getfn) _((Param));
- void (*setfn) _((Param, zlong));
- void (*unsetfn) _((Param, int));
+ zlong (*getfn) (Param);
+ void (*setfn) (Param, zlong);
+ void (*unsetfn) (Param, int);
};
struct gsu_float {
- double (*getfn) _((Param));
- void (*setfn) _((Param, double));
- void (*unsetfn) _((Param, int));
+ double (*getfn) (Param);
+ void (*setfn) (Param, double);
+ void (*unsetfn) (Param, int);
};
struct gsu_array {
- char **(*getfn) _((Param));
- void (*setfn) _((Param, char **));
- void (*unsetfn) _((Param, int));
+ char **(*getfn) (Param);
+ void (*setfn) (Param, char **);
+ void (*unsetfn) (Param, int);
};
struct gsu_hash {
- HashTable (*getfn) _((Param));
- void (*setfn) _((Param, HashTable));
- void (*unsetfn) _((Param, int));
+ HashTable (*getfn) (Param);
+ void (*setfn) (Param, HashTable);
+ void (*unsetfn) (Param, int);
};
@@ -1862,8 +1852,9 @@ struct param {
GsuHash h;
} gsu;
- int base; /* output base or floating point prec */
- int width; /* field width */
+ int base; /* output base or floating point prec or */
+ /* for namerefs, locallevel of reference */
+ int width; /* field width or nameref subscript idx */
char *env; /* location in environment, if exported */
char *ename; /* name of corresponding environment var */
Param old; /* old struct for use with local */
@@ -1942,9 +1933,13 @@ struct tieddata {
*/
#define PM_HASHELEM (1<<28) /* is a hash-element */
#define PM_NAMEDDIR (1<<29) /* has a corresponding nameddirtab entry */
+#define PM_NAMEREF (1<<30) /* pointer to a different parameter */
+
+#define PM_SELFREF PM_UNIQUE /* Overload when namerefs resolved */
+#define PM_NEWREF PM_SINGLE /* Overload in for-loop namerefs */
/* The option string corresponds to the first of the variables above */
-#define TYPESET_OPTSTR "aiEFALRZlurtxUhHTkz"
+#define TYPESET_OPTSTR "aiEFALRZlurtxUhHT"
/* These typeset options take an optional numeric argument */
#define TYPESET_OPTNUM "LRZiEF"
@@ -1967,6 +1962,10 @@ struct tieddata {
* elements
*/
#define SCANPM_CHECKING (1<<10) /* Check if set, no need to create */
+#define SCANPM_NOEXEC (1<<11) /* No command substitutions, etc. */
+#define SCANPM_NONAMESPC (1<<12) /* namespace syntax not allowed */
+#define SCANPM_NONAMEREF (1<<13) /* named references are not followed */
+
/* "$foo[@]"-style substitution
* Only sign bit is significant
*/
@@ -2185,6 +2184,7 @@ typedef groupset *Groupset;
#define PRINT_LINE (1<<6)
#define PRINT_POSIX_EXPORT (1<<7)
#define PRINT_POSIX_READONLY (1<<8)
+#define PRINT_WITH_NAMESPACE (1<<9)
/* flags for printing for the whence builtin */
#define PRINT_WHENCE_CSH (1<<7)
@@ -2220,8 +2220,6 @@ enum noerrexit_bits {
NOERREXIT_EXIT = 1,
/* Suppress ERR_RETURN: per function call */
NOERREXIT_RETURN = 2,
- /* NOERREXIT only needed on way down */
- NOERREXIT_UNTIL_EXEC = 4,
/* Force exit on SIGINT */
NOERREXIT_SIGNAL = 8
};
@@ -2658,22 +2656,25 @@ struct ttyinfo {
#define TCDELLINE 16
#define TCNEXTTAB 17
#define TCBOLDFACEBEG 18
-#define TCSTANDOUTBEG 19
-#define TCUNDERLINEBEG 20
-#define TCALLATTRSOFF 21
-#define TCSTANDOUTEND 22
-#define TCUNDERLINEEND 23
-#define TCHORIZPOS 24
-#define TCUPCURSOR 25
-#define TCDOWNCURSOR 26
-#define TCLEFTCURSOR 27
-#define TCRIGHTCURSOR 28
-#define TCSAVECURSOR 29
-#define TCRESTRCURSOR 30
-#define TCBACKSPACE 31
-#define TCFGCOLOUR 32
-#define TCBGCOLOUR 33
-#define TC_COUNT 34
+#define TCFAINTBEG 19
+#define TCSTANDOUTBEG 20
+#define TCUNDERLINEBEG 21
+#define TCITALICSBEG 22
+#define TCALLATTRSOFF 23
+#define TCSTANDOUTEND 24
+#define TCUNDERLINEEND 25
+#define TCITALICSEND 26
+#define TCHORIZPOS 27
+#define TCUPCURSOR 28
+#define TCDOWNCURSOR 29
+#define TCLEFTCURSOR 30
+#define TCRIGHTCURSOR 31
+#define TCSAVECURSOR 32
+#define TCRESTRCURSOR 33
+#define TCBACKSPACE 34
+#define TCFGCOLOUR 35
+#define TCBGCOLOUR 36
+#define TC_COUNT 37
#define tccan(X) (tclen[X])
@@ -2688,38 +2689,27 @@ struct ttyinfo {
#endif
#define TXTBOLDFACE 0x0001
-#define TXTSTANDOUT 0x0002
-#define TXTUNDERLINE 0x0004
-#define TXTFGCOLOUR 0x0008
-#define TXTBGCOLOUR 0x0010
-
-#define TXT_ATTR_ON_MASK 0x001F
-
-#define txtisset(X) (txtattrmask & (X))
-#define txtset(X) (txtattrmask |= (X))
-#define txtunset(X) (txtattrmask &= ~(X))
-
-#define TXTNOBOLDFACE 0x0020
-#define TXTNOSTANDOUT 0x0040
-#define TXTNOUNDERLINE 0x0080
-#define TXTNOFGCOLOUR 0x0100
-#define TXTNOBGCOLOUR 0x0200
-
-#define TXT_ATTR_OFF_MASK 0x03E0
-/* Bits to shift off right to get on */
-#define TXT_ATTR_OFF_ON_SHIFT 5
-#define TXT_ATTR_OFF_FROM_ON(attr) \
- (((attr) & TXT_ATTR_ON_MASK) << TXT_ATTR_OFF_ON_SHIFT)
-#define TXT_ATTR_ON_FROM_OFF(attr) \
- (((attr) & TXT_ATTR_OFF_MASK) >> TXT_ATTR_OFF_ON_SHIFT)
+#define TXTFAINT 0x0002
+#define TXTSTANDOUT 0x0004
+#define TXTUNDERLINE 0x0008
+#define TXTITALIC 0x0010
+#define TXTFGCOLOUR 0x0020
+#define TXTBGCOLOUR 0x0040
+
+#define TXT_ATTR_ALL 0x007F
+
/*
* Indicates to zle_refresh.c that the character entry is an
* index into the list of multiword symbols.
*/
#define TXT_MULTIWORD_MASK 0x0400
-/* used when, e.g an invalid colour is specified */
-#define TXT_ERROR 0x0800
+/* Used when, e.g an invalid colour is specified. Also used in REFRESH_ELEMENT
+ * to indicate that attributes should remain unchanged. */
+#define TXT_ERROR 0xF00000F000000003
+
+/* Mask for font weight */
+#define TXT_ATTR_FONT_WEIGHT (TXTBOLDFACE|TXTFAINT)
/* Mask for colour to use in foreground */
#define TXT_ATTR_FG_COL_MASK 0x000000FFFFFF0000
@@ -2735,26 +2725,19 @@ struct ttyinfo {
/* Flag to indicate that background is a 24-bit colour */
#define TXT_ATTR_BG_24BIT 0x8000
-/* Things to turn on, including values for the colour elements */
-#define TXT_ATTR_ON_VALUES_MASK \
- (TXT_ATTR_ON_MASK|TXT_ATTR_FG_COL_MASK|TXT_ATTR_BG_COL_MASK|\
- TXT_ATTR_FG_24BIT|TXT_ATTR_BG_24BIT)
-
/* Mask out everything to do with setting a foreground colour */
-#define TXT_ATTR_FG_ON_MASK \
+#define TXT_ATTR_FG_MASK \
(TXTFGCOLOUR|TXT_ATTR_FG_COL_MASK|TXT_ATTR_FG_24BIT)
/* Mask out everything to do with setting a background colour */
-#define TXT_ATTR_BG_ON_MASK \
+#define TXT_ATTR_BG_MASK \
(TXTBGCOLOUR|TXT_ATTR_BG_COL_MASK|TXT_ATTR_BG_24BIT)
/* Mask out everything to do with activating colours */
-#define TXT_ATTR_COLOUR_ON_MASK \
- (TXT_ATTR_FG_ON_MASK|TXT_ATTR_BG_ON_MASK)
+#define TXT_ATTR_COLOUR_MASK \
+ (TXT_ATTR_FG_MASK|TXT_ATTR_BG_MASK)
-#define txtchangeisset(T,X) ((T) & (X))
#define txtchangeget(T,A) (((T) & A ## _MASK) >> A ## _SHIFT)
-#define txtchangeset(T, X, Y) ((void)(T && (*T &= ~(Y), *T |= (X))))
/*
* For outputting sequences to change colour: specify foreground
@@ -2762,7 +2745,6 @@ struct ttyinfo {
*/
#define COL_SEQ_FG (0)
#define COL_SEQ_BG (1)
-#define COL_SEQ_COUNT (2)
struct color_rgb {
unsigned int red, green, blue;
@@ -2778,11 +2760,7 @@ enum {
/* Raw output: use stdout rather than shout */
TSC_RAW = 0x0001,
/* Output to current prompt buffer: only used when assembling prompt */
- TSC_PROMPT = 0x0002,
- /* Mask to get the output mode */
- TSC_OUTPUT_MASK = 0x0003,
- /* Change needs reset of other attributes */
- TSC_DIRTY = 0x0004
+ TSC_PROMPT = 0x0002
};
/****************************************/
@@ -3006,7 +2984,7 @@ enum errflag_bits {
/* Sorting */
/***********/
-typedef int (*CompareFn) _((const void *, const void *));
+typedef int (*CompareFn) (const void *, const void *);
enum {
SORTIT_ANYOLDHOW = 0, /* Defaults */
@@ -3064,13 +3042,13 @@ struct hist_stack {
short *chwords;
int chwordlen;
int chwordpos;
- int (*hgetc) _((void));
- void (*hungetc) _((int));
- void (*hwaddc) _((int));
- void (*hwbegin) _((int));
- void (*hwabort) _((void));
- void (*hwend) _((void));
- void (*addtoline) _((int));
+ int (*hgetc) (void);
+ void (*hungetc) (int);
+ void (*hwaddc) (int);
+ void (*hwbegin) (int);
+ void (*hwabort) (void);
+ void (*hwend) (void);
+ void (*addtoline) (int);
unsigned char *cstack;
int csp;
int hist_keep_comment;
@@ -3240,7 +3218,7 @@ enum {
/* compctl entry point pointers */
-typedef int (*CompctlReadFn) _((char *, char **, Options, char *));
+typedef int (*CompctlReadFn) (char *, char **, Options, char *);
/* ZLE entry point pointer */
diff --git a/Src/zsh_system.h b/Src/zsh_system.h
index 6f4efce96..21446a9b1 100644
--- a/Src/zsh_system.h
+++ b/Src/zsh_system.h
@@ -82,12 +82,6 @@
*/
#define _STRPTIME_DONTZERO
-#ifdef PROTOTYPES
-# define _(Args) Args
-#else
-# define _(Args) ()
-#endif
-
#ifndef HAVE_ALLOCA
# define alloca zhalloc
#else
@@ -101,7 +95,7 @@
# pragma alloca
# else
# ifndef alloca
-char *alloca _((size_t));
+char *alloca (size_t);
# endif
# endif
# endif
@@ -375,8 +369,6 @@ struct timespec {
# ifndef TIME_H_SELECT_H_CONFLICTS
# include <sys/select.h>
# endif
-#elif defined(SELECT_IN_SYS_SOCKET_H)
-# include <sys/socket.h>
#endif
#if defined(__APPLE__) && defined(HAVE_SELECT)
@@ -783,7 +775,8 @@ extern char **environ;
* We always need setenv and unsetenv in pairs, because
* we don't know how to do memory management on the values set.
*/
-#if defined(HAVE_SETENV) && defined(HAVE_UNSETENV) && !defined(__APPLE__)
+#if defined(HAVE_SETENV) && defined(HAVE_UNSETENV) \
+ && !defined(SETENV_MANGLES_EQUAL)
# define USE_SET_UNSET_ENV
#endif
@@ -808,16 +801,6 @@ extern short ospeed;
#endif
#endif
-/* Can't support job control without working tcsetgrp() */
-#ifdef BROKEN_TCSETPGRP
-#undef JOB_CONTROL
-#endif /* BROKEN_TCSETPGRP */
-
-#ifdef BROKEN_KILL_ESRCH
-#undef ESRCH
-#define ESRCH EINVAL
-#endif /* BROKEN_KILL_ESRCH */
-
/* Can we do locale stuff? */
#undef USE_LOCALE
#if defined(CONFIG_LOCALE) && defined(HAVE_SETLOCALE) && defined(LC_ALL)
diff --git a/Src/ztype.h b/Src/ztype.h
index 5c85b0cd7..4675f73a9 100644
--- a/Src/ztype.h
+++ b/Src/ztype.h
@@ -43,7 +43,8 @@
#define IWSEP (1 << 13)
#define INULL (1 << 14)
#define IPATTERN (1 << 15)
-#define zistype(X,Y) (typtab[STOUC(X)] & Y)
+#define INAMESPC (1 << 16)
+#define zistype(X,Y) (typtab[(unsigned char) (X)] & Y)
#define idigit(X) zistype(X,IDIGIT)
#define ialnum(X) zistype(X,IALNUM)
#define iblank(X) zistype(X,IBLANK) /* blank, not including \n */