summaryrefslogtreecommitdiff
path: root/Src
diff options
context:
space:
mode:
Diffstat (limited to 'Src')
-rw-r--r--Src/Builtins/rlimits.awk116
-rw-r--r--Src/Builtins/rlimits.c700
-rw-r--r--Src/Builtins/rlimits.mdd15
-rw-r--r--Src/Modules/curses.c4
-rw-r--r--Src/Modules/curses_keys.awk7
-rw-r--r--Src/Modules/datetime.c5
-rw-r--r--Src/Modules/db_gdbm.c4
-rw-r--r--Src/Modules/files.c44
-rw-r--r--Src/Modules/nearcolor.c2
-rw-r--r--Src/Modules/param_private.c12
-rw-r--r--Src/Modules/parameter.c110
-rw-r--r--Src/Modules/system.c84
-rw-r--r--Src/Modules/watch.c (renamed from Src/watch.c)163
-rw-r--r--Src/Modules/watch.mdd7
-rw-r--r--Src/Modules/zprof.c31
-rw-r--r--Src/Modules/zpty.c11
-rw-r--r--Src/Modules/zutil.c82
-rw-r--r--Src/Zle/comp.h9
-rw-r--r--Src/Zle/compcore.c146
-rw-r--r--Src/Zle/complete.c23
-rw-r--r--Src/Zle/complist.c38
-rw-r--r--Src/Zle/compmatch.c15
-rw-r--r--Src/Zle/compresult.c15
-rw-r--r--Src/Zle/computil.c114
-rw-r--r--Src/Zle/zle.h4
-rw-r--r--Src/Zle/zle_hist.c9
-rw-r--r--Src/Zle/zle_keymap.c2
-rw-r--r--Src/Zle/zle_main.c8
-rw-r--r--Src/Zle/zle_misc.c9
-rw-r--r--Src/Zle/zle_move.c2
-rw-r--r--Src/Zle/zle_refresh.c64
-rw-r--r--Src/Zle/zle_thingy.c19
-rw-r--r--Src/Zle/zle_utils.c23
-rw-r--r--Src/builtin.c136
-rw-r--r--Src/compat.c28
-rw-r--r--Src/exec.c136
-rw-r--r--Src/glob.c87
-rw-r--r--Src/hashnameddir.c125
-rw-r--r--Src/hashtable.c107
-rw-r--r--Src/hist.c99
-rw-r--r--Src/init.c31
-rw-r--r--Src/input.c25
-rw-r--r--Src/jobs.c91
-rw-r--r--Src/lex.c24
-rw-r--r--Src/linklist.c13
-rw-r--r--Src/loop.c12
-rw-r--r--Src/makepro.awk2
-rw-r--r--Src/math.c23
-rw-r--r--Src/mem.c51
-rw-r--r--Src/module.c2
-rw-r--r--Src/openssh_bsd_setres_id.c4
-rw-r--r--Src/options.c6
-rw-r--r--Src/params.c103
-rw-r--r--Src/parse.c112
-rw-r--r--Src/patchlevel.h.release2
-rw-r--r--Src/pattern.c11
-rw-r--r--Src/prompt.c67
-rw-r--r--Src/signals.c20
-rw-r--r--Src/sort.c25
-rw-r--r--Src/string.c5
-rw-r--r--Src/subst.c115
-rw-r--r--Src/text.c4
-rw-r--r--Src/utils.c432
-rw-r--r--Src/zsh.h95
-rw-r--r--Src/zsh.mdd2
-rw-r--r--Src/zsh_system.h21
66 files changed, 2357 insertions, 1556 deletions
diff --git a/Src/Builtins/rlimits.awk b/Src/Builtins/rlimits.awk
deleted file mode 100644
index e9c576c66..000000000
--- a/Src/Builtins/rlimits.awk
+++ /dev/null
@@ -1,116 +0,0 @@
-#
-# rlimits.awk: {g,n}awk script to generate rlimits.h
-#
-# NB: On SunOS 4.1.3 - user-functions don't work properly, also \" problems
-# Without 0 + hacks some nawks compare numbers as strings
-#
-BEGIN {limidx = 0}
-
-/^[\t ]*(#[\t ]*define[\t _]*RLIMIT_[A-Z_]*[\t ]*[0-9][0-9]*|RLIMIT_[A-Z_]*,[\t ]*|_*RLIMIT_[A-Z_]*[\t ]*=[\t ]*[0-9][0-9]*,[\t ]*)/ {
- limindex = index($0, "RLIMIT_")
- limtail = substr($0, limindex, 80)
- split(limtail, tmp)
- limnam = substr(tmp[1], 8, 20)
- limnum = tmp[2]
- # in this case I assume GNU libc resourcebits.h
- if (limnum == "") {
- limnum = limidx++
- limindex = index($0, ",")
- limnam = substr(limnam, 1, limindex-1)
- }
- if (limnum == "=") {
- if (tmp[3] ~ /^[0-9]/) {
- limnum = tmp[3] + 0
- } else {
- limnum = limidx++
- }
- limindex = index($0, ",")
- limnam = substr(limnam, 1, limindex-1)
- }
- limrev[limnam] = limnum
- if (lim[limnum] == "") {
- lim[limnum] = limnam
- if (limnum ~ /^[0-9]*$/) {
- if (limnam == "AIO_MEM") { msg[limnum] = "Maiomemorylocked" }
- if (limnam == "AIO_OPS") { msg[limnum] = "Naiooperations" }
- if (limnam == "AS") { msg[limnum] = "Maddressspace" }
- if (limnam == "CORE") { msg[limnum] = "Mcoredumpsize" }
- if (limnam == "CPU") { msg[limnum] = "Tcputime" }
- if (limnam == "DATA") { msg[limnum] = "Mdatasize" }
- if (limnam == "FSIZE") { msg[limnum] = "Mfilesize" }
- if (limnam == "LOCKS") { msg[limnum] = "Nmaxfilelocks" }
- if (limnam == "MEMLOCK") { msg[limnum] = "Mmemorylocked" }
- if (limnam == "NOFILE") { msg[limnum] = "Ndescriptors" }
- if (limnam == "NPROC") { msg[limnum] = "Nmaxproc" }
- if (limnam == "NTHR") { msg[limnum] = "Nmaxpthreads" }
- if (limnam == "OFILE") { msg[limnum] = "Ndescriptors" }
- if (limnam == "PTHREAD") { msg[limnum] = "Nmaxpthreads" }
- if (limnam == "RSS") { msg[limnum] = "Mresident" }
- if (limnam == "SBSIZE") { msg[limnum] = "Msockbufsize" }
- if (limnam == "STACK") { msg[limnum] = "Mstacksize" }
- if (limnam == "TCACHE") { msg[limnum] = "Ncachedthreads" }
- if (limnam == "VMEM") { msg[limnum] = "Mvmemorysize" }
- if (limnam == "SIGPENDING") { msg[limnum] = "Nsigpending" }
- if (limnam == "MSGQUEUE") { msg[limnum] = "Nmsgqueue" }
- if (limnam == "NICE") { msg[limnum] = "Nnice" }
- if (limnam == "RTPRIO") { msg[limnum] = "Nrt_priority" }
- if (limnam == "RTTIME") { msg[limnum] = "Urt_time" }
- if (limnam == "POSIXLOCKS") { msg[limnum] = "Nposixlocks" }
- if (limnam == "NPTS") { msg[limnum] = "Npseudoterminals" }
- if (limnam == "SWAP") { msg[limnum] = "Mswapsize" }
- if (limnam == "KQUEUES") { msg[limnum] = "Nkqueues" }
- if (limnam == "UMTXP") { msg[limnum] = "Numtxp" }
- }
- }
-}
-/^[\t ]*#[\t ]*define[\t _]*RLIM_NLIMITS[\t ]*[0-9][0-9]*/ {
- limindex = index($0, "RLIM_")
- limtail = substr($0, limindex, 80)
- split(limtail, tmp)
- nlimits = tmp[2]
-}
-# in case of GNU libc
-/^[\t ]*RLIM_NLIMITS[\t ]*=[\t ]*RLIMIT_NLIMITS/ {
- if(!nlimits) { nlimits = limidx }
-}
-/^[\t _]*RLIM(IT)?_NLIMITS[\t ]*=[\t ]*[0-9][0-9]*/ {
- limindex = index($0, "=")
- limtail = substr($0, limindex, 80)
- split(limtail, tmp)
- nlimits = tmp[2]
-}
-
-END {
- if (limrev["MEMLOCK"] != "") {
- irss = limrev["RSS"]
- msg[irss] = "Mmemoryuse"
- }
- ps = "%s"
-
- printf("%s\n%s\n\n", "/** rlimits.h **/", "/** architecture-customized limits for zsh **/")
- printf("#define ZSH_NLIMITS %d\n\nstatic char const *recs[ZSH_NLIMITS] = {\n", 0 + nlimits)
-
- for (i = 0; i < 0 + nlimits; i++)
- if (msg[i] == "")
- printf("\t%c%s%c,\n", 34, lim[i], 34)
- else
- printf("\t%c%s%c,\n", 34, substr(msg[i], 2, 30), 34)
- print "};"
- print ""
- print "static int limtype[ZSH_NLIMITS] = {"
- for (i = 0; i < 0 + nlimits; i++) {
- if (msg[i] == "")
- limtype = "UNKNOWN"
- else {
- limtype = substr(msg[i], 1, 1)
- if(limtype == "M") { limtype = "MEMORY" }
- if(limtype == "N") { limtype = "NUMBER" }
- if(limtype == "T") { limtype = "TIME" }
- if(limtype == "U") { limtype = "MICROSECONDS" }
- }
- printf("\tZLIMTYPE_%s,\n", limtype)
- }
- print "};"
-
- exit(0)
-}
diff --git a/Src/Builtins/rlimits.c b/Src/Builtins/rlimits.c
index 6b552f3a9..5f9c84b0f 100644
--- a/Src/Builtins/rlimits.c
+++ b/Src/Builtins/rlimits.c
@@ -32,20 +32,7 @@
#if defined(HAVE_GETRLIMIT) && defined(RLIM_INFINITY)
-#if defined(HAVE_RLIMIT_POSIXLOCKS) && !defined(HAVE_RLIMIT_LOCKS)
-# define RLIMIT_LOCKS RLIMIT_POSIXLOCKS
-# define HAVE_RLIMIT_LOCKS 1
-#endif
-
-#if defined(HAVE_RLIMIT_NTHR) && !defined(HAVE_RLIMIT_PTHREAD)
-# define RLIMIT_PTHREAD RLIMIT_NTHR
-# define HAVE_RLIMIT_PTHREAD 1
-# define THREAD_FMT "-T: threads "
-#else
-# define THREAD_FMT "-T: threads per process "
-#endif
-
-enum {
+enum zlimtype {
ZLIMTYPE_MEMORY,
ZLIMTYPE_NUMBER,
ZLIMTYPE_TIME,
@@ -53,11 +40,222 @@ enum {
ZLIMTYPE_UNKNOWN
};
-/* Generated rec array containing limits required for the limit builtin. *
- * They must appear in this array in numerical order of the RLIMIT_* macros. */
+typedef struct resinfo_T {
+ int res; /* RLIMIT_XXX */
+ char* name; /* used by limit builtin */
+ enum zlimtype type;
+ int unit; /* 1, 512, or 1024 */
+ char opt; /* option character */
+ char* descr; /* used by ulimit builtin */
+} resinfo_T;
+
+/* table of known resources */
+/*
+ * How to add a new resource:
+ * 1. Add zsh_LIMIT_PRESENT(RLIMIT_XXX) in configure.ac.
+ * 2. Add an entry for RLIMIT_XXX to known_resources[].
+ * Make sure the option letter (resinto_T.opt) is unique.
+ * 3. Build zsh and run the test B12rlimit.ztst.
+ */
+static const resinfo_T known_resources[] = {
+ {RLIMIT_CPU, "cputime", ZLIMTYPE_TIME, 1,
+ 't', "cpu time (seconds)"},
+ {RLIMIT_FSIZE, "filesize", ZLIMTYPE_MEMORY, 512,
+ 'f', "file size (blocks)"},
+ {RLIMIT_DATA, "datasize", ZLIMTYPE_MEMORY, 1024,
+ 'd', "data seg size (kbytes)"},
+ {RLIMIT_STACK, "stacksize", ZLIMTYPE_MEMORY, 1024,
+ 's', "stack size (kbytes)"},
+ {RLIMIT_CORE, "coredumpsize", ZLIMTYPE_MEMORY, 512,
+ 'c', "core file size (blocks)"},
+# ifdef HAVE_RLIMIT_NOFILE
+ {RLIMIT_NOFILE, "descriptors", ZLIMTYPE_NUMBER, 1,
+ 'n', "file descriptors"},
+# endif
+# if defined(HAVE_RLIMIT_AS) && !defined(RLIMIT_VMEM_IS_AS)
+ {RLIMIT_AS, "addressspace", ZLIMTYPE_MEMORY, 1024,
+ 'v', "address space (kbytes)"},
+# endif
+# if defined(HAVE_RLIMIT_RSS) && !defined(RLIMIT_VMEM_IS_RSS) && !defined(RLIMIT_RSS_IS_AS)
+ {RLIMIT_RSS, "resident", ZLIMTYPE_MEMORY, 1024,
+ 'm', "resident set size (kbytes)"},
+# endif
+# if defined(HAVE_RLIMIT_VMEM)
+ {RLIMIT_VMEM,
+# if defined(RLIMIT_VMEM_IS_RSS)
+ "resident", ZLIMTYPE_MEMORY, 1024,
+ 'm', "memory size (kbytes)"
+# else
+ "vmemorysize", ZLIMTYPE_MEMORY, 1024,
+ 'v', "virtual memory size (kbytes)"
+# endif
+ },
+# endif
+# ifdef HAVE_RLIMIT_NPROC
+ {RLIMIT_NPROC, "maxproc", ZLIMTYPE_NUMBER, 1,
+ 'u', "processes"},
+# endif
+# ifdef HAVE_RLIMIT_MEMLOCK
+ {RLIMIT_MEMLOCK, "memorylocked", ZLIMTYPE_MEMORY, 1024,
+ 'l', "locked-in-memory size (kbytes)"},
+# endif
+ /* Linux */
+# ifdef HAVE_RLIMIT_LOCKS
+ {RLIMIT_LOCKS, "maxfilelocks", ZLIMTYPE_NUMBER, 1,
+ 'x', "file locks"},
+# endif
+# ifdef HAVE_RLIMIT_SIGPENDING
+ {RLIMIT_SIGPENDING, "sigpending", ZLIMTYPE_NUMBER, 1,
+ 'i', "pending signals"},
+# endif
+# ifdef HAVE_RLIMIT_MSGQUEUE
+ {RLIMIT_MSGQUEUE, "msgqueue", ZLIMTYPE_NUMBER, 1,
+ 'q', "bytes in POSIX msg queues"},
+# endif
+# ifdef HAVE_RLIMIT_NICE
+ {RLIMIT_NICE, "nice", ZLIMTYPE_NUMBER, 1,
+ 'e', "max nice"},
+# endif
+# ifdef HAVE_RLIMIT_RTPRIO
+ {RLIMIT_RTPRIO, "rt_priority", ZLIMTYPE_NUMBER, 1,
+ 'r', "max rt priority"},
+# endif
+# ifdef HAVE_RLIMIT_RTTIME
+ {RLIMIT_RTTIME, "rt_time", ZLIMTYPE_MICROSECONDS, 1,
+ 'N', "rt cpu time (microseconds)"},
+# endif
+ /* BSD */
+# ifdef HAVE_RLIMIT_SBSIZE
+ {RLIMIT_SBSIZE, "sockbufsize", ZLIMTYPE_MEMORY, 1,
+ 'b', "socket buffer size (bytes)"},
+# endif
+# ifdef HAVE_RLIMIT_KQUEUES /* FreeBSD */
+ {RLIMIT_KQUEUES, "kqueues", ZLIMTYPE_NUMBER, 1,
+ 'k', "kqueues"},
+# endif
+# ifdef HAVE_RLIMIT_NPTS /* FreeBSD */
+ {RLIMIT_NPTS, "pseudoterminals", ZLIMTYPE_NUMBER, 1,
+ 'p', "pseudo-terminals"},
+# endif
+# ifdef HAVE_RLIMIT_SWAP /* FreeBSD */
+ {RLIMIT_SWAP, "swapsize", ZLIMTYPE_MEMORY, 1024,
+ 'w', "swap size (kbytes)"},
+# endif
+# ifdef HAVE_RLIMIT_UMTXP /* FreeBSD */
+ {RLIMIT_UMTXP, "umtxp", ZLIMTYPE_NUMBER, 1,
+ 'o', "umtx shared locks"},
+# endif
+
+# ifdef HAVE_RLIMIT_POSIXLOCKS /* DragonFly */
+ {RLIMIT_POSIXLOCKS, "posixlocks", ZLIMTYPE_NUMBER, 1,
+ 'x', "number of POSIX locks"},
+# endif
+# if defined(HAVE_RLIMIT_NTHR) && !defined(HAVE_RLIMIT_RTPRIO) /* Net/OpenBSD */
+ {RLIMIT_NTHR, "maxpthreads", ZLIMTYPE_NUMBER, 1,
+ 'r', "threads"},
+# endif
+ /* others */
+# if defined(HAVE_RLIMIT_PTHREAD) && !defined(HAVE_RLIMIT_NTHR) /* IRIX ? */
+ {RLIMIT_PTHREAD, "maxpthreads", ZLIMTYPE_NUMBER, 1,
+ 'T', "threads per process"},
+# endif
+# ifdef HAVE_RLIMIT_AIO_MEM /* HP-UX ? */
+ {RLIMIT_AIO_MEM, "aiomemorylocked", ZLIMTYPE_MEMORY, 1024,
+ 'N', "AIO locked-in-memory (kbytes)"},
+# endif
+# ifdef HAVE_RLIMIT_AIO_OPS /* HP-UX ? */
+ {RLIMIT_AIO_OPS, "aiooperations", ZLIMTYPE_NUMBER, 1,
+ 'N', "AIO operations"},
+# endif
+# ifdef HAVE_RLIMIT_TCACHE /* HP-UX ? */
+ {RLIMIT_TCACHE, "cachedthreads", ZLIMTYPE_NUMBER, 1,
+ 'N', "cached threads"},
+# endif
+};
+
+/* resinfo[RLIMIT_XXX] points to the corresponding entry
+ * in known_resources[] */
+static const resinfo_T **resinfo;
-# include "rlimits.h"
+/**/
+static void
+set_resinfo(void)
+{
+ int i;
+
+ resinfo = (const resinfo_T **)zshcalloc(RLIM_NLIMITS*sizeof(resinfo_T *));
+
+ for (i=0; i<sizeof(known_resources)/sizeof(resinfo_T); ++i) {
+ resinfo[known_resources[i].res] = &known_resources[i];
+ }
+ for (i=0; i<RLIM_NLIMITS; ++i) {
+ if (!resinfo[i]) {
+ /* unknown resource */
+ resinfo_T *info = (resinfo_T *)zshcalloc(sizeof(resinfo_T));
+ char *buf = (char *)zalloc(12);
+ snprintf(buf, 12, "UNKNOWN-%d", i);
+ info->res = - 1; /* negative value indicates "unknown" */
+ info->name = buf;
+ info->type = ZLIMTYPE_UNKNOWN;
+ info->unit = 1;
+ info->opt = 'N';
+ info->descr = buf;
+ resinfo[i] = info;
+ }
+ }
+}
+/**/
+static void
+free_resinfo(void)
+{
+ int i;
+ for (i=0; i<RLIM_NLIMITS; ++i) {
+ if (resinfo[i]->res < 0) { /* unknown resource */
+ free(resinfo[i]->name);
+ free((void*)resinfo[i]);
+ }
+ }
+ free(resinfo);
+ resinfo = NULL;
+}
+
+/* Find resource by its option character */
+
+/**/
+static int
+find_resource(char c)
+{
+ int i;
+ for (i=0; i<RLIM_NLIMITS; ++i) {
+ if (resinfo[i]->opt == c)
+ return i;
+ }
+ return -1;
+}
+
+/* Print a value of type rlim_t */
+
+/**/
+static void
+printrlim(rlim_t val, const char *unit)
+{
+# ifdef RLIM_T_IS_QUAD_T
+ printf("%qd%s", val, unit);
+# else
+# ifdef RLIM_T_IS_LONG_LONG
+ printf("%lld%s", val, unit);
+# else
+# ifdef RLIM_T_IS_UNSIGNED
+ printf("%lu%s", (unsigned long)val, unit);
+# else
+ printf("%ld%s", (long)val, unit);
+# endif /* RLIM_T_IS_UNSIGNED */
+# endif /* RLIM_T_IS_LONG_LONG */
+# endif /* RLIM_T_IS_QUAD_T */
+}
+
+/**/
static rlim_t
zstrtorlimt(const char *s, char **t, int base)
{
@@ -97,8 +295,8 @@ static void
showlimitvalue(int lim, rlim_t val)
{
/* display limit for resource number lim */
- if (lim < ZSH_NLIMITS)
- printf("%-16s", recs[lim]);
+ if (lim < RLIM_NLIMITS)
+ printf("%-16s", resinfo[lim]->name);
else
{
/* Unknown limit, hence unknown units. */
@@ -106,81 +304,25 @@ showlimitvalue(int lim, rlim_t val)
}
if (val == RLIM_INFINITY)
printf("unlimited\n");
- else if (lim >= ZSH_NLIMITS)
- {
-# ifdef RLIM_T_IS_QUAD_T
- printf("%qd\n", val);
-# else
-# ifdef RLIM_T_IS_LONG_LONG
- printf("%lld\n", val);
-# else
-# ifdef RLIM_T_IS_UNSIGNED
- printf("%lu\n", (unsigned long)val);
-# else
- printf("%ld\n", (long)val);
-# endif /* RLIM_T_IS_UNSIGNED */
-# endif /* RLIM_T_IS_LONG_LONG */
-# endif /* RLIM_T_IS_QUAD_T */
- }
- else if (limtype[lim] == ZLIMTYPE_TIME) {
+ else if (lim >= RLIM_NLIMITS)
+ printrlim(val, "\n");
+ else if (resinfo[lim]->type == ZLIMTYPE_TIME) {
/* time-type resource -- display as hours, minutes and
seconds. */
printf("%d:%02d:%02d\n", (int)(val / 3600),
(int)(val / 60) % 60, (int)(val % 60));
- } else if (limtype[lim] == ZLIMTYPE_MICROSECONDS) {
- /* microseconds */
-# ifdef RLIM_T_IS_QUAD_T
- printf("%qdus\n", val);
-# else
-# ifdef RLIM_T_IS_LONG_LONG
- printf("%lldus\n", val);
-# else
-# ifdef RLIM_T_IS_UNSIGNED
- printf("%luus\n", (unsigned long)val);
-# else
- printf("%ldus\n", (long)val);
-# endif /* RLIM_T_IS_UNSIGNED */
-# endif /* RLIM_T_IS_LONG_LONG */
-# endif /* RLIM_T_IS_QUAD_T */
- } else if (limtype[lim] == ZLIMTYPE_NUMBER ||
- limtype[lim] == ZLIMTYPE_UNKNOWN) {
- /* pure numeric resource */
-# ifdef RLIM_T_IS_QUAD_T
- printf("%qd\n", val);
-# else
-# ifdef RLIM_T_IS_LONG_LONG
- printf("%lld\n", val);
-# else
-# ifdef RLIM_T_IS_UNSIGNED
- printf("%lu\n", (unsigned long)val);
-# else
- printf("%ld\n", (long)val);
-# endif /* RLIM_T_IS_UNSIGNED */
-# endif /* RLIM_T_IS_LONG_LONG */
-# endif /* RLIM_T_IS_QUAD_T */
- } else if (val >= 1024L * 1024L)
- /* memory resource -- display with `K' or `M' modifier */
-# ifdef RLIM_T_IS_QUAD_T
- printf("%qdMB\n", val / (1024L * 1024L));
- else
- printf("%qdkB\n", val / 1024L);
-# else
-# ifdef RLIM_T_IS_LONG_LONG
- printf("%lldMB\n", val / (1024L * 1024L));
- else
- printf("%lldkB\n", val / 1024L);
-# else
-# ifdef RLIM_T_IS_UNSIGNED
- printf("%luMB\n", (unsigned long)(val / (1024L * 1024L)));
- else
- printf("%lukB\n", (unsigned long)(val / 1024L));
-# else
- printf("%ldMB\n", (long)val / (1024L * 1024L));
- else
- printf("%ldkB\n", (long)val / 1024L);
-# endif /* RLIM_T_IS_UNSIGNED */
-# endif /* RLIM_T_IS_LONG_LONG */
-# endif /* RLIM_T_IS_QUAD_T */
+ } else if (resinfo[lim]->type == ZLIMTYPE_MICROSECONDS)
+ printrlim(val, "us\n"); /* microseconds */
+ else if (resinfo[lim]->type == ZLIMTYPE_NUMBER ||
+ resinfo[lim]->type == ZLIMTYPE_UNKNOWN)
+ printrlim(val, "\n"); /* pure numeric resource */
+ else {
+ /* memory resource -- display with `k' or `M' modifier */
+ if (val >= 1024L * 1024L)
+ printrlim(val/(1024L * 1024L), "MB\n");
+ else
+ printrlim(val/1024L, "kB\n");
+ }
}
/* Display resource limits. hard indicates whether `hard' or `soft' *
@@ -193,7 +335,7 @@ showlimits(char *nam, int hard, int lim)
{
int rt;
- if (lim >= ZSH_NLIMITS)
+ if (lim >= RLIM_NLIMITS)
{
/*
* Not configured into the shell. Ask the OS
@@ -215,7 +357,7 @@ showlimits(char *nam, int hard, int lim)
else
{
/* main loop over resource types */
- for (rt = 0; rt != ZSH_NLIMITS; rt++)
+ for (rt = 0; rt != RLIM_NLIMITS; rt++)
showlimitvalue(rt, (hard) ? limits[rt].rlim_max :
limits[rt].rlim_cur);
}
@@ -234,7 +376,7 @@ printulimit(char *nam, int lim, int hard, int head)
rlim_t limit;
/* get the limit in question */
- if (lim >= ZSH_NLIMITS)
+ if (lim >= RLIM_NLIMITS)
{
struct rlimit vals;
@@ -248,199 +390,25 @@ printulimit(char *nam, int lim, int hard, int head)
else
limit = (hard) ? limits[lim].rlim_max : limits[lim].rlim_cur;
/* display the appropriate heading */
- switch (lim) {
- case RLIMIT_CORE:
- if (head)
- printf("-c: core file size (blocks) ");
- if (limit != RLIM_INFINITY)
- limit /= 512;
- break;
- case RLIMIT_DATA:
- if (head)
- printf("-d: data seg size (kbytes) ");
- if (limit != RLIM_INFINITY)
- limit /= 1024;
- break;
- case RLIMIT_FSIZE:
- if (head)
- printf("-f: file size (blocks) ");
- if (limit != RLIM_INFINITY)
- limit /= 512;
- break;
-# ifdef HAVE_RLIMIT_SIGPENDING
- case RLIMIT_SIGPENDING:
- if (head)
- printf("-i: pending signals ");
- break;
-# endif
-# ifdef HAVE_RLIMIT_MEMLOCK
- case RLIMIT_MEMLOCK:
- if (head)
- printf("-l: locked-in-memory size (kbytes) ");
- if (limit != RLIM_INFINITY)
- limit /= 1024;
- break;
-# endif /* HAVE_RLIMIT_MEMLOCK */
-/* If RLIMIT_VMEM and RLIMIT_RSS are defined and equal, avoid *
- * duplicate case statement. Observed on QNX Neutrino 6.1.0. */
-# if defined(HAVE_RLIMIT_RSS) && !defined(RLIMIT_VMEM_IS_RSS) && !defined(RLIMIT_RSS_IS_AS)
- case RLIMIT_RSS:
- if (head)
- printf("-m: resident set size (kbytes) ");
- if (limit != RLIM_INFINITY)
- limit /= 1024;
- break;
-# endif /* HAVE_RLIMIT_RSS */
-# if defined(HAVE_RLIMIT_VMEM) && defined(HAVE_RLIMIT_RSS) && defined(RLIMIT_VMEM_IS_RSS)
- case RLIMIT_VMEM:
- if (head)
- printf("-m: memory size (kbytes) ");
- if (limit != RLIM_INFINITY)
- limit /= 1024;
- break;
-# endif /* HAVE_RLIMIT_VMEM */
-# ifdef HAVE_RLIMIT_NOFILE
- case RLIMIT_NOFILE:
- if (head)
- printf("-n: file descriptors ");
- break;
-# endif /* HAVE_RLIMIT_NOFILE */
-# ifdef HAVE_RLIMIT_MSGQUEUE
- case RLIMIT_MSGQUEUE:
- if (head)
- printf("-q: bytes in POSIX msg queues ");
- break;
-# endif
- case RLIMIT_STACK:
- if (head)
- printf("-s: stack size (kbytes) ");
- if (limit != RLIM_INFINITY)
- limit /= 1024;
- break;
- case RLIMIT_CPU:
- if (head)
- printf("-t: cpu time (seconds) ");
- break;
-# ifdef HAVE_RLIMIT_NPROC
- case RLIMIT_NPROC:
- if (head)
- printf("-u: processes ");
- break;
-# endif /* HAVE_RLIMIT_NPROC */
-# if defined(HAVE_RLIMIT_VMEM) && (!defined(HAVE_RLIMIT_RSS) || !defined(RLIMIT_VMEM_IS_RSS))
- case RLIMIT_VMEM:
- if (head)
- printf("-v: virtual memory size (kbytes) ");
- if (limit != RLIM_INFINITY)
- limit /= 1024;
- break;
-# endif /* HAVE_RLIMIT_VMEM */
-# if defined HAVE_RLIMIT_AS && !defined(RLIMIT_VMEM_IS_AS)
- case RLIMIT_AS:
- if (head)
- printf("-v: address space (kbytes) ");
- if (limit != RLIM_INFINITY)
- limit /= 1024;
- break;
-# endif /* HAVE_RLIMIT_AS */
-# ifdef HAVE_RLIMIT_LOCKS
- case RLIMIT_LOCKS:
- if (head)
- printf("-x: file locks ");
- break;
-# endif /* HAVE_RLIMIT_LOCKS */
-# ifdef HAVE_RLIMIT_AIO_MEM
- case RLIMIT_AIO_MEM:
- if (head)
- printf("-N %2d: AIO locked-in-memory (kbytes)", RLIMIT_AIO_MEM);
- if (limit != RLIM_INFINITY)
- limit /= 1024;
- break;
-# endif /* HAVE_RLIMIT_AIO_MEM */
-# ifdef HAVE_RLIMIT_AIO_OPS
- case RLIMIT_AIO_OPS:
- if (head)
- printf("-N %2d: AIO operations ", RLIMIT_AIO_OPS);
- break;
-# endif /* HAVE_RLIMIT_AIO_OPS */
-# ifdef HAVE_RLIMIT_TCACHE
- case RLIMIT_TCACHE:
- if (head)
- printf("-N %2d: cached threads ", RLIMIT_TCACHE);
- break;
-# endif /* HAVE_RLIMIT_TCACHE */
-# ifdef HAVE_RLIMIT_SBSIZE
- case RLIMIT_SBSIZE:
- if (head)
- printf("-b: socket buffer size (bytes) ");
- break;
-# endif /* HAVE_RLIMIT_SBSIZE */
-# ifdef HAVE_RLIMIT_PTHREAD
- case RLIMIT_PTHREAD:
- if (head)
- printf("%s", THREAD_FMT);
- break;
-# endif /* HAVE_RLIMIT_PTHREAD */
-# ifdef HAVE_RLIMIT_NICE
- case RLIMIT_NICE:
- if (head)
- printf("-e: max nice ");
- break;
-# endif /* HAVE_RLIMIT_NICE */
-# ifdef HAVE_RLIMIT_RTPRIO
- case RLIMIT_RTPRIO:
- if (head)
- printf("-r: max rt priority ");
- break;
-# endif /* HAVE_RLIMIT_RTPRIO */
-# ifdef HAVE_RLIMIT_NPTS
- case RLIMIT_NPTS:
- if (head)
- printf("-p: pseudo-terminals ");
- break;
-# endif /* HAVE_RLIMIT_NPTS */
-# ifdef HAVE_RLIMIT_SWAP
- case RLIMIT_SWAP:
- if (head)
- printf("-w: swap size (kbytes) ");
- if (limit != RLIM_INFINITY)
- limit /= 1024;
- break;
-# endif /* HAVE_RLIMIT_SWAP */
-# ifdef HAVE_RLIMIT_KQUEUES
- case RLIMIT_KQUEUES:
- if (head)
- printf("-k: kqueues ");
- break;
-# endif /* HAVE_RLIMIT_KQUEUES */
-# ifdef HAVE_RLIMIT_UMTXP
- case RLIMIT_UMTXP:
- if (head)
- printf("-o: umtx shared locks ");
- break;
-# endif /* HAVE_RLIMIT_UMTXP */
- default:
- if (head)
- printf("-N %2d: ", lim);
- break;
+ if (head) {
+ if (lim < RLIM_NLIMITS) {
+ const resinfo_T *info = resinfo[lim];
+ if (info->opt == 'N')
+ printf("-N %2d: %-29s", lim, info->descr);
+ else
+ printf("-%c: %-32s", info->opt, info->descr);
+ }
+ else
+ printf("-N %2d: %-29s", lim, "");
}
/* display the limit */
if (limit == RLIM_INFINITY)
printf("unlimited\n");
else {
-# ifdef RLIM_T_IS_QUAD_T
- printf("%qd\n", limit);
-# else
-# ifdef RLIM_T_IS_LONG_LONG
- printf("%lld\n", limit);
-# else
-# ifdef RLIM_T_IS_UNSIGNED
- printf("%lu\n", (unsigned long)limit);
-# else
- printf("%ld\n", (long)limit);
-# endif /* RLIM_T_IS_UNSIGNED */
-# endif /* RLIM_T_IS_LONG_LONG */
-# endif /* RLIM_T_IS_QUAD_T */
+ if (lim < RLIM_NLIMITS)
+ printrlim(limit/resinfo[lim]->unit, "\n");
+ else
+ printrlim(limit, "\n");
}
return 0;
@@ -450,7 +418,7 @@ printulimit(char *nam, int lim, int hard, int head)
static int
do_limit(char *nam, int lim, rlim_t val, int hard, int soft, int set)
{
- if (lim >= ZSH_NLIMITS) {
+ if (lim >= RLIM_NLIMITS) {
struct rlimit vals;
if (getrlimit(lim, &vals) < 0)
{
@@ -558,8 +526,8 @@ bin_limit(char *nam, char **argv, Options ops, UNUSED(int func))
lim = (int)zstrtol(s, NULL, 10);
}
else
- for (lim = -1, limnum = 0; limnum < ZSH_NLIMITS; limnum++)
- if (!strncmp(recs[limnum], s, strlen(s))) {
+ for (lim = -1, limnum = 0; limnum < RLIM_NLIMITS; limnum++)
+ if (!strncmp(resinfo[limnum]->name, s, strlen(s))) {
if (lim != -1)
lim = -2;
else
@@ -576,7 +544,7 @@ bin_limit(char *nam, char **argv, Options ops, UNUSED(int func))
/* without value for limit, display the current limit */
if (!(s = *argv++))
return showlimits(nam, hard, lim);
- if (lim >= ZSH_NLIMITS)
+ if (lim >= RLIM_NLIMITS)
{
val = zstrtorlimt(s, &s, 10);
if (*s)
@@ -586,7 +554,7 @@ bin_limit(char *nam, char **argv, Options ops, UNUSED(int func))
return 1;
}
}
- else if (limtype[lim] == ZLIMTYPE_TIME) {
+ else if (resinfo[lim]->type == ZLIMTYPE_TIME) {
/* time-type resource -- may be specified as seconds, or minutes or *
* hours with the `m' and `h' modifiers, and `:' may be used to add *
* together more than one of these. It's easier to understand from *
@@ -604,9 +572,9 @@ bin_limit(char *nam, char **argv, Options ops, UNUSED(int func))
return 1;
}
}
- } else if (limtype[lim] == ZLIMTYPE_NUMBER ||
- limtype[lim] == ZLIMTYPE_UNKNOWN ||
- limtype[lim] == ZLIMTYPE_MICROSECONDS) {
+ } else if (resinfo[lim]->type == ZLIMTYPE_NUMBER ||
+ resinfo[lim]->type == ZLIMTYPE_UNKNOWN ||
+ resinfo[lim]->type == ZLIMTYPE_MICROSECONDS) {
/* pure numeric resource -- only a straight decimal number is
permitted. */
char *t = s;
@@ -642,7 +610,7 @@ static int
do_unlimit(char *nam, int lim, int hard, int soft, int set, int euid)
{
/* remove specified limit */
- if (lim >= ZSH_NLIMITS) {
+ if (lim >= RLIM_NLIMITS) {
struct rlimit vals;
if (getrlimit(lim, &vals) < 0)
{
@@ -718,8 +686,8 @@ bin_unlimit(char *nam, char **argv, Options ops, UNUSED(int func))
if (idigit(**argv)) {
lim = (int)zstrtol(*argv, NULL, 10);
} else {
- for (lim = -1, limnum = 0; limnum < ZSH_NLIMITS; limnum++)
- if (!strncmp(recs[limnum], *argv, strlen(*argv))) {
+ for (lim = -1, limnum = 0; limnum < RLIM_NLIMITS; limnum++)
+ if (!strncmp(resinfo[limnum]->name, *argv, strlen(*argv))) {
if (lim != -1)
lim = -2;
else
@@ -800,116 +768,14 @@ bin_ulimit(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
resmask = (1 << RLIM_NLIMITS) - 1;
nres = RLIM_NLIMITS;
continue;
- case 't':
- res = RLIMIT_CPU;
- break;
- case 'f':
- res = RLIMIT_FSIZE;
- break;
- case 'd':
- res = RLIMIT_DATA;
- break;
- case 's':
- res = RLIMIT_STACK;
- break;
- case 'c':
- res = RLIMIT_CORE;
- break;
-# ifdef HAVE_RLIMIT_SBSIZE
- case 'b':
- res = RLIMIT_SBSIZE;
- break;
-# endif /* HAVE_RLIMIT_SBSIZE */
-# ifdef HAVE_RLIMIT_MEMLOCK
- case 'l':
- res = RLIMIT_MEMLOCK;
- break;
-# endif /* HAVE_RLIMIT_MEMLOCK */
-# ifdef HAVE_RLIMIT_RSS
- case 'm':
- res = RLIMIT_RSS;
- break;
-# endif /* HAVE_RLIMIT_RSS */
-# ifdef HAVE_RLIMIT_NOFILE
- case 'n':
- res = RLIMIT_NOFILE;
- break;
-# endif /* HAVE_RLIMIT_NOFILE */
-# ifdef HAVE_RLIMIT_NPROC
- case 'u':
- res = RLIMIT_NPROC;
- break;
-# endif /* HAVE_RLIMIT_NPROC */
-# if defined(HAVE_RLIMIT_VMEM) || defined(HAVE_RLIMIT_AS)
- case 'v':
-# ifdef HAVE_RLIMIT_VMEM
- res = RLIMIT_VMEM;
-# else
- res = RLIMIT_AS;
-# endif
- break;
-# endif /* HAVE_RLIMIT_VMEM */
-# ifdef HAVE_RLIMIT_LOCKS
- case 'x':
- res = RLIMIT_LOCKS;
- break;
-# endif
-# ifdef HAVE_RLIMIT_SIGPENDING
- case 'i':
- res = RLIMIT_SIGPENDING;
- break;
-# endif
-# ifdef HAVE_RLIMIT_MSGQUEUE
- case 'q':
- res = RLIMIT_MSGQUEUE;
- break;
-# endif
-# ifdef HAVE_RLIMIT_NICE
- case 'e':
- res = RLIMIT_NICE;
- break;
-# endif
-# ifdef HAVE_RLIMIT_RTPRIO
- case 'r':
- res = RLIMIT_RTPRIO;
- break;
-# else
-# ifdef HAVE_RLIMIT_NTHR
- /* For compatibility with sh on NetBSD */
- case 'r':
- res = RLIMIT_NTHR;
- break;
-# endif /* HAVE_RLIMIT_NTHR */
-# endif
-# ifdef HAVE_RLIMIT_NPTS
- case 'p':
- res = RLIMIT_NPTS;
- break;
-# endif
-# ifdef HAVE_RLIMIT_SWAP
- case 'w':
- res = RLIMIT_SWAP;
- break;
-# endif
-# ifdef HAVE_RLIMIT_KQUEUES
- case 'k':
- res = RLIMIT_KQUEUES;
- break;
-# endif
-# ifdef HAVE_RLIMIT_PTHREAD
- case 'T':
- res = RLIMIT_PTHREAD;
- break;
-# endif
-# ifdef HAVE_RLIMIT_UMTXP
- case 'o':
- res = RLIMIT_UMTXP;
- break;
-# endif
default:
- /* unrecognised limit */
- zwarnnam(name, "bad option: -%c", *options);
- return 1;
+ res = find_resource(*options);
+ if (res < 0) {
+ /* unrecognised limit */
+ zwarnnam(name, "bad option: -%c", *options);
+ return 1;
+ }
+ break;
}
if (options[1]) {
resmask |= 1 << res;
@@ -961,34 +827,8 @@ bin_ulimit(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
return 1;
}
/* scale appropriately */
- switch (res) {
- case RLIMIT_FSIZE:
- case RLIMIT_CORE:
- limit *= 512;
- break;
- case RLIMIT_DATA:
- case RLIMIT_STACK:
-# ifdef HAVE_RLIMIT_RSS
- case RLIMIT_RSS:
-# endif /* HAVE_RLIMIT_RSS */
-# ifdef HAVE_RLIMIT_MEMLOCK
- case RLIMIT_MEMLOCK:
-# endif /* HAVE_RLIMIT_MEMLOCK */
-/* If RLIMIT_VMEM and RLIMIT_RSS are defined and equal, avoid *
- * duplicate case statement. Observed on QNX Neutrino 6.1.0. */
-# if defined(HAVE_RLIMIT_VMEM) && !defined(RLIMIT_VMEM_IS_RSS)
- case RLIMIT_VMEM:
-# endif /* HAVE_RLIMIT_VMEM */
-/* ditto RLIMIT_VMEM and RLIMIT_AS */
-# if defined(HAVE_RLIMIT_AS) && !defined(RLIMIT_VMEM_IS_AS) && !defined(RLIMIT_RSS_IS_AS)
- case RLIMIT_AS:
-# endif /* HAVE_RLIMIT_AS */
-# ifdef HAVE_RLIMIT_AIO_MEM
- case RLIMIT_AIO_MEM:
-# endif /* HAVE_RLIMIT_AIO_MEM */
- limit *= 1024;
- break;
- }
+ if (res < RLIM_NLIMITS)
+ limit *= resinfo[res]->unit;
}
if (do_limit(name, res, limit, hard, soft, 1))
ret++;
@@ -1052,6 +892,7 @@ enables_(Module m, int **enables)
int
boot_(UNUSED(Module m))
{
+ set_resinfo();
return 0;
}
@@ -1059,6 +900,7 @@ boot_(UNUSED(Module m))
int
cleanup_(Module m)
{
+ free_resinfo();
return setfeatureenables(m, &module_features, NULL);
}
diff --git a/Src/Builtins/rlimits.mdd b/Src/Builtins/rlimits.mdd
index 9e6e9e598..06c9e9c7f 100644
--- a/Src/Builtins/rlimits.mdd
+++ b/Src/Builtins/rlimits.mdd
@@ -6,18 +6,3 @@ autofeatures="b:limit b:ulimit b:unlimit"
autofeatures_emu="b:ulimit"
objects="rlimits.o"
-
-:<<\Make
-rlimits.o rlimits..o: rlimits.h
-
-# this file will not be made if limits are unavailable
-rlimits.h: rlimits.awk @RLIMITS_INC_H@
- $(AWK) -f $(sdir)/rlimits.awk @RLIMITS_INC_H@ /dev/null > rlimits.h
- @if grep ZLIMTYPE_UNKNOWN rlimits.h >/dev/null; then \
- echo >&2 WARNING: unknown limits: mail Src/Builtins/rlimits.h to developers; \
- else :; fi
-
-clean-here: clean.rlimits
-clean.rlimits:
- rm -f rlimits.h
-Make
diff --git a/Src/Modules/curses.c b/Src/Modules/curses.c
index 19f285e34..e46903916 100644
--- a/Src/Modules/curses.c
+++ b/Src/Modules/curses.c
@@ -1212,7 +1212,7 @@ zccmd_input(const char *nam, char **args)
addlinknode(margs, "CTRL");
if (mevent.bstate & BUTTON_ALT)
addlinknode(margs, "ALT");
- if (!setaparam(args[3], zlinklist2array(margs)))
+ if (!setaparam(args[3], zlinklist2array(margs, 1)))
return 1;
} else {
#endif
@@ -1464,7 +1464,7 @@ zccmd_querychar(const char *nam, char **args)
}
/* Turn this into an array and store it. */
- return !setaparam(args[1] ? args[1] : "reply", zlinklist2array(clist));
+ return !setaparam(args[1] ? args[1] : "reply", zlinklist2array(clist, 1));
}
diff --git a/Src/Modules/curses_keys.awk b/Src/Modules/curses_keys.awk
index ffb182c35..25bd63c7e 100644
--- a/Src/Modules/curses_keys.awk
+++ b/Src/Modules/curses_keys.awk
@@ -12,8 +12,13 @@ BEGIN {nkeydefs = 0}
END {
printf("static const struct zcurses_namenumberpair keypad_names[] = {\n")
- for (i = 0; i < 0 + nkeydefs; i++)
+ for (i = 0; i < 0 + nkeydefs; i++) {
+ if (name[i] == "EVENT")
+ printf("#ifdef KEY_EVENT\n")
printf(" {\"%s\", KEY_%s},\n", name[i], name[i])
+ if (name[i] == "EVENT")
+ printf("#endif\n")
+ }
printf(" {NULL, 0}\n")
printf("};\n")
}
diff --git a/Src/Modules/datetime.c b/Src/Modules/datetime.c
index 521c15a5b..085e4cc26 100644
--- a/Src/Modules/datetime.c
+++ b/Src/Modules/datetime.c
@@ -175,7 +175,8 @@ output_strftime(char *nam, char **argv, Options ops, UNUSED(int func))
setsparam(scalar, metafy(buffer, len, META_DUP));
} else {
fwrite(buffer, 1, len, stdout);
- putchar('\n');
+ if (!OPT_ISSET(ops,'n'))
+ putchar('\n');
}
zfree(buffer, bufsize);
@@ -235,7 +236,7 @@ getcurrenttime(UNUSED(Param pm))
}
static struct builtin bintab[] = {
- BUILTIN("strftime", 0, bin_strftime, 1, 3, 0, "qrs:", NULL),
+ BUILTIN("strftime", 0, bin_strftime, 1, 3, 0, "nqrs:", NULL),
};
static const struct gsu_integer epochseconds_gsu =
diff --git a/Src/Modules/db_gdbm.c b/Src/Modules/db_gdbm.c
index b8e7c76c6..7e11ec939 100644
--- a/Src/Modules/db_gdbm.c
+++ b/Src/Modules/db_gdbm.c
@@ -233,7 +233,7 @@ bin_zuntie(char *nam, char **args, Options ops, UNUSED(int func))
/**/
static int
-bin_zgdbmpath(char *nam, char **args, Options ops, UNUSED(int func))
+bin_zgdbmpath(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
{
Param pm;
char *pmname;
@@ -332,6 +332,8 @@ gdbmgetfn(Param pm)
/* Can return pointer, correctly saved inside hash */
return pm->u.str;
+ } else {
+ pm->node.flags |= PM_DEFAULTED;
}
/* Free key */
diff --git a/Src/Modules/files.c b/Src/Modules/files.c
index 6d20e38a8..bf0e8f8a8 100644
--- a/Src/Modules/files.c
+++ b/Src/Modules/files.c
@@ -32,12 +32,6 @@
typedef int (*MoveFunc) _((char const *, char const *));
typedef int (*RecurseFunc) _((char *, char *, struct stat const *, void *));
-#ifndef STDC_HEADERS
-extern int link _((const char *, const char *));
-extern int symlink _((const char *, const char *));
-extern int rename _((const char *, const char *));
-#endif
-
struct recursivecmd;
#include "files.pro"
@@ -122,19 +116,29 @@ domkdir(char *nam, char *path, mode_t mode, int p)
{
int err;
mode_t oumask;
+ struct stat st;
+ int n = 8;
char const *rpath = unmeta(path);
- if(p) {
- struct stat st;
-
- if(!stat(rpath, &st) && S_ISDIR(st.st_mode))
+ while(n-- > 0) {
+ oumask = umask(0);
+ err = mkdir(rpath, mode) ? errno : 0;
+ umask(oumask);
+ if (!err)
return 0;
+ if(!p || err != EEXIST)
+ break;
+ if(stat(rpath, &st)) {
+ if(errno == ENOENT)
+ continue;
+ err = errno;
+ break;
+ }
+ if(S_ISDIR(st.st_mode))
+ return 0;
+ break;
}
- oumask = umask(0);
- err = mkdir(rpath, mode) ? errno : 0;
- umask(oumask);
- if(!err)
- return 0;
+
zwarnnam(nam, "cannot make directory `%s': %e", path, err);
return 1;
}
@@ -342,7 +346,13 @@ domove(char *nam, MoveFunc movefn, char *p, char *q, int flags)
unlink(qbuf);
}
if(movefn(pbuf, qbuf)) {
- zwarnnam(nam, "%s: %e", p, errno);
+ int ferrno = errno;
+ char *errfile = p;
+ if (ferrno == ENOENT && !lstat(pbuf, &st)) {
+ /* p *does* exist, so error is in q */
+ errfile = q;
+ }
+ zwarnnam(nam, "`%s': %e", errfile, ferrno);
zsfree(pbuf);
return 1;
}
@@ -642,7 +652,7 @@ chmod_dochmod(char *arg, char *rp, UNUSED(struct stat const *sp), void *magic)
/**/
static int
-bin_chmod(char *nam, char **args, Options ops, int func)
+bin_chmod(char *nam, char **args, Options ops, UNUSED(int func))
{
struct chmodmagic chm;
char *str = args[0], *ptr;
diff --git a/Src/Modules/nearcolor.c b/Src/Modules/nearcolor.c
index b49ee9afb..d50a6bb44 100644
--- a/Src/Modules/nearcolor.c
+++ b/Src/Modules/nearcolor.c
@@ -188,7 +188,7 @@ enables_(Module m, int **enables)
/**/
int
-boot_(Module m)
+boot_(UNUSED(Module m))
{
addhookfunc("get_color_attr", (Hookfn) getnearestcolor);
return 0;
diff --git a/Src/Modules/param_private.c b/Src/Modules/param_private.c
index 86416c5c5..c53839152 100644
--- a/Src/Modules/param_private.c
+++ b/Src/Modules/param_private.c
@@ -125,7 +125,7 @@ makeprivate(HashNode hn, UNUSED(int flags))
break;
}
/* PM_HIDE so new parameters in deeper scopes do not shadow */
- pm->node.flags |= (PM_HIDE|PM_SPECIAL|PM_REMOVABLE);
+ pm->node.flags |= (PM_HIDE|PM_SPECIAL|PM_REMOVABLE|PM_RO_BY_DESIGN);
pm->level -= 1;
}
}
@@ -171,6 +171,7 @@ bin_private(char *nam, char **args, LinkList assigns, Options ops, int func)
{
int from_typeset = 1;
int ofake = fakelevel; /* paranoia in case of recursive call */
+ int hasargs = /* *args != NULL || */ (assigns && firstnode(assigns));
makeprivate_error = 0;
if (!OPT_ISSET(ops, 'P')) {
@@ -189,7 +190,12 @@ bin_private(char *nam, char **args, LinkList assigns, Options ops, int func)
return bin_typeset("private", args, assigns, ops, func);
}
- ops->ind['g'] = 2; /* force bin_typeset() to behave as "local" */
+ if (!(OPT_ISSET(ops, 'm') || OPT_ISSET(ops, '+')))
+ ops->ind['g'] = 2; /* force bin_typeset() to behave as "local" */
+ if (OPT_ISSET(ops, 'p') || OPT_ISSET(ops, 'm') ||
+ (!hasargs && OPT_ISSET(ops, '+'))) {
+ return bin_typeset("private", args, assigns, ops, func);
+ }
queue_signals();
fakelevel = locallevel;
@@ -555,7 +561,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:%lprtux", "P")
+ BUILTIN("private", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_private, 0, -1, 0, "AE:%F:%HL:%PR:%TUZ:%ahi:%lmprtux", "P")
};
static struct features module_features = {
diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c
index ef9148d7b..dbb61e474 100644
--- a/Src/Modules/parameter.c
+++ b/Src/Modules/parameter.c
@@ -592,7 +592,7 @@ getpmfunction_source(HashTable ht, const char *name)
return getfunction_source(ht, name, 0);
}
-/* Param table entry for retrieving ds_functions_source element */
+/* Param table entry for retrieving dis_functions_source element */
/**/
static HashNode
@@ -1244,19 +1244,19 @@ histwgetfn(UNUSED(Param pm))
/**/
static char *
-pmjobtext(int job)
+pmjobtext(Job jtab, int job)
{
Process pn;
int len = 1;
char *ret;
- for (pn = jobtab[job].procs; pn; pn = pn->next)
+ for (pn = jtab[job].procs; pn; pn = pn->next)
len += strlen(pn->text) + 3;
ret = (char *) zhalloc(len);
ret[0] = '\0';
- for (pn = jobtab[job].procs; pn; pn = pn->next) {
+ for (pn = jtab[job].procs; pn; pn = pn->next) {
strcat(ret, pn->text);
if (pn->next)
strcat(ret, " | ");
@@ -1269,22 +1269,25 @@ static HashNode
getpmjobtext(UNUSED(HashTable ht), const char *name)
{
Param pm = NULL;
- int job;
+ int job, jmax;
char *pend;
+ Job jtab;
pm = (Param) hcalloc(sizeof(struct param));
pm->node.nam = dupstring(name);
pm->node.flags = PM_SCALAR | PM_READONLY;
pm->gsu.s = &nullsetscalar_gsu;
+ selectjobtab(&jtab, &jmax);
+
job = strtod(name, &pend);
/* Non-numeric keys are looked up by job name */
if (*pend)
job = getjob(name, NULL);
- if (job >= 1 && job <= maxjob &&
- jobtab[job].stat && jobtab[job].procs &&
- !(jobtab[job].stat & STAT_NOPRINT))
- pm->u.str = pmjobtext(job);
+ if (job >= 1 && job <= jmax &&
+ jtab[job].stat && jtab[job].procs &&
+ !(jtab[job].stat & STAT_NOPRINT))
+ pm->u.str = pmjobtext(jtab, job);
else {
pm->u.str = dupstring("");
pm->node.flags |= (PM_UNSET|PM_SPECIAL);
@@ -1297,22 +1300,25 @@ static void
scanpmjobtexts(UNUSED(HashTable ht), ScanFunc func, int flags)
{
struct param pm;
- int job;
+ int job, jmax;
char buf[40];
+ Job jtab;
memset((void *)&pm, 0, sizeof(struct param));
pm.node.flags = PM_SCALAR | PM_READONLY;
pm.gsu.s = &nullsetscalar_gsu;
- for (job = 1; job <= maxjob; job++) {
- if (jobtab[job].stat && jobtab[job].procs &&
- !(jobtab[job].stat & STAT_NOPRINT)) {
+ selectjobtab(&jtab, &jmax);
+
+ for (job = 1; job <= jmax; job++) {
+ if (jtab[job].stat && jtab[job].procs &&
+ !(jtab[job].stat & STAT_NOPRINT)) {
if (func != scancountparams) {
sprintf(buf, "%d", job);
pm.node.nam = dupstring(buf);
if ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
!(flags & SCANPM_WANTKEYS))
- pm.u.str = pmjobtext(job);
+ pm.u.str = pmjobtext(jtab, job);
}
func(&pm.node, flags);
}
@@ -1323,7 +1329,7 @@ scanpmjobtexts(UNUSED(HashTable ht), ScanFunc func, int flags)
/**/
static char *
-pmjobstate(int job)
+pmjobstate(Job jtab, int job)
{
Process pn;
char buf[256], buf2[128], *ret, *state, *cp;
@@ -1335,14 +1341,14 @@ pmjobstate(int job)
else
cp = ":";
- if (jobtab[job].stat & STAT_DONE)
+ if (jtab[job].stat & STAT_DONE)
ret = dyncat("done", cp);
- else if (jobtab[job].stat & STAT_STOPPED)
+ else if (jtab[job].stat & STAT_STOPPED)
ret = dyncat("suspended", cp);
else
ret = dyncat("running", cp);
- for (pn = jobtab[job].procs; pn; pn = pn->next) {
+ for (pn = jtab[job].procs; pn; pn = pn->next) {
if (pn->status == SP_RUNNING)
state = "running";
@@ -1371,21 +1377,24 @@ static HashNode
getpmjobstate(UNUSED(HashTable ht), const char *name)
{
Param pm = NULL;
- int job;
+ int job, jmax;
char *pend;
+ Job jtab;
pm = (Param) hcalloc(sizeof(struct param));
pm->node.nam = dupstring(name);
pm->node.flags = PM_SCALAR | PM_READONLY;
pm->gsu.s = &nullsetscalar_gsu;
+ selectjobtab(&jtab, &jmax);
+
job = strtod(name, &pend);
if (*pend)
job = getjob(name, NULL);
- if (job >= 1 && job <= maxjob &&
- jobtab[job].stat && jobtab[job].procs &&
- !(jobtab[job].stat & STAT_NOPRINT))
- pm->u.str = pmjobstate(job);
+ if (job >= 1 && job <= jmax &&
+ jtab[job].stat && jtab[job].procs &&
+ !(jtab[job].stat & STAT_NOPRINT))
+ pm->u.str = pmjobstate(jtab, job);
else {
pm->u.str = dupstring("");
pm->node.flags |= (PM_UNSET|PM_SPECIAL);
@@ -1398,22 +1407,25 @@ static void
scanpmjobstates(UNUSED(HashTable ht), ScanFunc func, int flags)
{
struct param pm;
- int job;
+ int job, jmax;
+ Job jtab;
char buf[40];
+ selectjobtab(&jtab, &jmax);
+
memset((void *)&pm, 0, sizeof(struct param));
pm.node.flags = PM_SCALAR | PM_READONLY;
pm.gsu.s = &nullsetscalar_gsu;
- for (job = 1; job <= maxjob; job++) {
- if (jobtab[job].stat && jobtab[job].procs &&
- !(jobtab[job].stat & STAT_NOPRINT)) {
+ for (job = 1; job <= jmax; job++) {
+ if (jtab[job].stat && jtab[job].procs &&
+ !(jtab[job].stat & STAT_NOPRINT)) {
if (func != scancountparams) {
sprintf(buf, "%d", job);
pm.node.nam = dupstring(buf);
if ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
!(flags & SCANPM_WANTKEYS))
- pm.u.str = pmjobstate(job);
+ pm.u.str = pmjobstate(jtab, job);
}
func(&pm.node, flags);
}
@@ -1424,11 +1436,11 @@ scanpmjobstates(UNUSED(HashTable ht), ScanFunc func, int flags)
/**/
static char *
-pmjobdir(int job)
+pmjobdir(Job jtab, int job)
{
char *ret;
- ret = dupstring(jobtab[job].pwd ? jobtab[job].pwd : pwd);
+ ret = dupstring(jtab[job].pwd ? jtab[job].pwd : pwd);
return ret;
}
@@ -1437,21 +1449,24 @@ static HashNode
getpmjobdir(UNUSED(HashTable ht), const char *name)
{
Param pm = NULL;
- int job;
+ int job, jmax;
char *pend;
+ Job jtab;
pm = (Param) hcalloc(sizeof(struct param));
pm->node.nam = dupstring(name);
pm->node.flags = PM_SCALAR | PM_READONLY;
pm->gsu.s = &nullsetscalar_gsu;
+ selectjobtab(&jtab, &jmax);
+
job = strtod(name, &pend);
if (*pend)
job = getjob(name, NULL);
- if (job >= 1 && job <= maxjob &&
- jobtab[job].stat && jobtab[job].procs &&
- !(jobtab[job].stat & STAT_NOPRINT))
- pm->u.str = pmjobdir(job);
+ if (job >= 1 && job <= jmax &&
+ jtab[job].stat && jtab[job].procs &&
+ !(jtab[job].stat & STAT_NOPRINT))
+ pm->u.str = pmjobdir(jtab, job);
else {
pm->u.str = dupstring("");
pm->node.flags |= (PM_UNSET|PM_SPECIAL);
@@ -1464,22 +1479,25 @@ static void
scanpmjobdirs(UNUSED(HashTable ht), ScanFunc func, int flags)
{
struct param pm;
- int job;
+ int job, jmax;
char buf[40];
+ Job jtab;
memset((void *)&pm, 0, sizeof(struct param));
pm.node.flags = PM_SCALAR | PM_READONLY;
pm.gsu.s = &nullsetscalar_gsu;
- for (job = 1; job <= maxjob; job++) {
- if (jobtab[job].stat && jobtab[job].procs &&
- !(jobtab[job].stat & STAT_NOPRINT)) {
+ selectjobtab(&jtab, &jmax);
+
+ for (job = 1; job <= jmax; job++) {
+ if (jtab[job].stat && jtab[job].procs &&
+ !(jtab[job].stat & STAT_NOPRINT)) {
if (func != scancountparams) {
sprintf(buf, "%d", job);
pm.node.nam = dupstring(buf);
if ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
!(flags & SCANPM_WANTKEYS))
- pm.u.str = pmjobdir(job);
+ pm.u.str = pmjobdir(jtab, job);
}
func(&pm.node, flags);
}
@@ -2011,6 +2029,9 @@ scanpmdissaliases(HashTable ht, ScanFunc func, int flags)
/**/
static Groupset get_all_groups(void)
{
+#ifdef DISABLE_DYNAMIC_NSS
+ return NULL;
+#else
Groupset gs = zhalloc(sizeof(*gs));
Groupmap gaptr;
gid_t *list, *lptr, egid;
@@ -2063,6 +2084,7 @@ static Groupset get_all_groups(void)
}
return gs;
+#endif /* DISABLE_DYNAMIC_NSS */
}
/* Standard hash element lookup. */
@@ -2081,7 +2103,11 @@ getpmusergroups(UNUSED(HashTable ht), const char *name)
pm->gsu.s = &nullsetscalar_gsu;
if (!gs) {
+#ifdef DISABLE_DYNAMIC_NSS
+ zerr("parameter 'usergroups' not available: NSS is disabled");
+#else
zerr("failed to retrieve groups for user: %e", errno);
+#endif
pm->u.str = dupstring("");
pm->node.flags |= (PM_UNSET|PM_SPECIAL);
return &pm->node;
@@ -2113,7 +2139,11 @@ scanpmusergroups(UNUSED(HashTable ht), ScanFunc func, int flags)
Groupmap gaptr;
if (!gs) {
+#ifdef DISABLE_DYNAMIC_NSS
+ zerr("parameter 'usergroups' not available: NSS is disabled");
+#else
zerr("failed to retrieve groups for user: %e", errno);
+#endif
return;
}
diff --git a/Src/Modules/system.c b/Src/Modules/system.c
index fb3d80773..71745548f 100644
--- a/Src/Modules/system.c
+++ b/Src/Modules/system.c
@@ -29,6 +29,7 @@
#include "system.mdh"
#include "system.pro"
+#include <math.h>
#ifdef HAVE_POLL_H
# include <poll.h>
@@ -279,7 +280,7 @@ bin_syswrite(char *nam, char **args, Options ops, UNUSED(int func))
}
-static struct { char *name; int oflag; } openopts[] = {
+static struct { const char *name; int oflag; } openopts[] = {
#ifdef O_CLOEXEC
{ "cloexec", O_CLOEXEC },
#else
@@ -296,6 +297,9 @@ static struct { char *name; int oflag; } openopts[] = {
#ifdef O_NOATIME
{ "noatime", O_NOATIME },
#endif
+#ifdef O_NONBLOCK
+ { "nonblock", O_NONBLOCK},
+#endif
{ "excl", O_EXCL | O_CREAT },
{ "creat", O_CREAT },
{ "create", O_CREAT },
@@ -531,7 +535,9 @@ static int
bin_zsystem_flock(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
{
int cloexec = 1, unlock = 0, readlock = 0;
- zlong timeout = -1;
+ double timeout = -1;
+ long timeout_interval = 1e6;
+ mnumber timeout_param;
char *fdvar = NULL;
#ifdef HAVE_FCNTL_H
struct flock lck;
@@ -583,7 +589,51 @@ bin_zsystem_flock(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
} else {
optarg = *args++;
}
- timeout = mathevali(optarg);
+ timeout_param = matheval(optarg);
+ timeout = (timeout_param.type & MN_FLOAT) ?
+ timeout_param.u.d : (double)timeout_param.u.l;
+
+ /*
+ * timeout must not overflow time_t, but little is known
+ * about this type's limits. Conservatively limit to 2^30-1
+ * (34 years). Then it can only overflow if time_t is only
+ * a 32-bit int and CLOCK_MONOTONIC is not supported, in which
+ * case there is a Y2038 problem anyway.
+ */
+ if (timeout > 1073741823.) {
+ zwarnnam(nam, "flock: invalid timeout value: '%s'",
+ optarg);
+ return 1;
+ }
+ break;
+
+ case 'i':
+ /* retry interval in seconds */
+ if (optptr[1]) {
+ optarg = optptr + 1;
+ optptr += strlen(optarg) - 1;
+ } else if (!*args) {
+ zwarnnam(nam,
+ "flock: option %c requires "
+ "a numeric retry interval",
+ opt);
+ return 1;
+ } else {
+ optarg = *args++;
+ }
+ timeout_param = matheval(optarg);
+ if (!(timeout_param.type & MN_FLOAT)) {
+ timeout_param.type = MN_FLOAT;
+ timeout_param.u.d = (double)timeout_param.u.l;
+ }
+ timeout_param.u.d = ceil(timeout_param.u.d * 1e6);
+ if (timeout_param.u.d < 1
+ || timeout_param.u.d > 0.999 * LONG_MAX) {
+ zwarnnam(nam, "flock: invalid interval value: '%s'",
+ optarg);
+ return 1;
+ }
+ timeout_interval = (long)timeout_param.u.d;
break;
case 'u':
@@ -647,7 +697,24 @@ bin_zsystem_flock(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
lck.l_len = 0; /* lock the whole file */
if (timeout > 0) {
- time_t end = time(NULL) + (time_t)timeout;
+ /*
+ * Get current time, calculate timeout time.
+ * No need to check for overflow, already checked above.
+ */
+ struct timespec now, end;
+ double timeout_s;
+ long remaining_us;
+ zgettime_monotonic_if_available(&now);
+ end.tv_sec = now.tv_sec;
+ end.tv_nsec = now.tv_nsec;
+ end.tv_nsec += ceil(modf(timeout, &timeout_s) * 1000000000L);
+ end.tv_sec += timeout_s;
+ if (end.tv_nsec >= 1000000000L) {
+ end.tv_nsec -= 1000000000L;
+ end.tv_sec += 1;
+ }
+
+ /* Try acquiring lock, loop until timeout. */
while (fcntl(flock_fd, F_SETLK, &lck) < 0) {
if (errflag) {
zclose(flock_fd);
@@ -658,11 +725,16 @@ bin_zsystem_flock(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
zwarnnam(nam, "failed to lock file %s: %e", args[0], errno);
return 1;
}
- if (time(NULL) >= end) {
+ zgettime_monotonic_if_available(&now);
+ remaining_us = timespec_diff_us(&now, &end);
+ if (remaining_us <= 0) {
zclose(flock_fd);
return 2;
}
- sleep(1);
+ if (remaining_us <= timeout_interval) {
+ timeout_interval = remaining_us;
+ }
+ zsleep(timeout_interval);
}
} else {
while (fcntl(flock_fd, timeout == 0 ? F_SETLK : F_SETLKW, &lck) < 0) {
diff --git a/Src/watch.c b/Src/Modules/watch.c
index 93b3cb134..d45c3cf3d 100644
--- a/Src/watch.c
+++ b/Src/Modules/watch.c
@@ -27,7 +27,7 @@
*
*/
-#include "zsh.mdh"
+#include "watch.mdh"
/* Headers for utmp/utmpx structures */
#ifdef HAVE_UTMP_H
@@ -139,9 +139,6 @@
# define DEFAULT_WATCHFMT "%n has %a %l."
#endif /* !WATCH_UTMP_UT_HOST */
-/**/
-char const * const default_watchfmt = DEFAULT_WATCHFMT;
-
#ifdef WATCH_STRUCT_UTMP
# include "watch.pro"
@@ -152,11 +149,14 @@ char const * const default_watchfmt = DEFAULT_WATCHFMT;
static int wtabsz = 0;
static WATCH_STRUCT_UTMP *wtab = NULL;
+
+/* the last time we checked the people in the WATCH variable */
+static time_t lastwatch;
+
static time_t lastutmpcheck = 0;
/* get the time of login/logout for WATCH */
-/**/
static time_t
getlogtime(WATCH_STRUCT_UTMP *u, int inout)
{
@@ -169,9 +169,9 @@ getlogtime(WATCH_STRUCT_UTMP *u, int inout)
return u->ut_time;
if (!(in = fopen(WATCH_WTMP_FILE, "r")))
return time(NULL);
- fseek(in, 0, 2);
+ fseek(in, 0, SEEK_END);
do {
- if (fseek(in, ((first) ? -1 : -2) * sizeof(WATCH_STRUCT_UTMP), 1)) {
+ if (fseek(in, ((first) ? -1 : -2) * sizeof(WATCH_STRUCT_UTMP), SEEK_CUR)) {
fclose(in);
return time(NULL);
}
@@ -202,7 +202,6 @@ getlogtime(WATCH_STRUCT_UTMP *u, int inout)
# define BEGIN3 '('
# define END3 ')'
-/**/
static char *
watch3ary(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt)
{
@@ -247,6 +246,7 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini)
struct tm *tm;
char *fm2;
int len;
+ zattr atr;
# ifdef WATCH_UTMP_UT_HOST
char *p;
int i;
@@ -348,6 +348,40 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini)
case '%':
putchar('%');
break;
+ case 'F':
+ if (*fmt == '{') {
+ fmt++;
+ 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);
+ }
+ }
+ break;
+ case 'f':
+ txtunset(TXT_ATTR_FG_ON_MASK);
+ set_colour_attribute(TXTNOFGCOLOUR, COL_SEQ_FG, TSC_RAW);
+ break;
+ case 'K':
+ if (*fmt == '{') {
+ fmt++;
+ 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);
+ }
+ }
+ break;
+ case 'k':
+ txtunset(TXT_ATTR_BG_ON_MASK);
+ set_colour_attribute(TXTNOBGCOLOUR, COL_SEQ_BG, TSC_RAW);
+ break;
case 'S':
txtset(TXTSTANDOUT);
tsetcap(TCSTANDOUTBEG, TSC_RAW);
@@ -407,7 +441,6 @@ watchlog_match(char *teststr, char *actual, int len)
/* check the List for login/logouts */
-/**/
static void
watchlog(int inout, WATCH_STRUCT_UTMP *u, char **w, char *fmt)
{
@@ -470,7 +503,6 @@ watchlog(int inout, WATCH_STRUCT_UTMP *u, char **w, char *fmt)
/* compare 2 utmp entries */
-/**/
static int
ucmp(WATCH_STRUCT_UTMP *u, WATCH_STRUCT_UTMP *v)
{
@@ -481,7 +513,6 @@ ucmp(WATCH_STRUCT_UTMP *u, WATCH_STRUCT_UTMP *v)
/* initialize the user List */
-/**/
static int
readwtab(WATCH_STRUCT_UTMP **head, int initial_sz)
{
@@ -592,10 +623,19 @@ dowatch(void)
wtab = utab;
wtabsz = utabsz;
fflush(stdout);
+ lastwatch = time(NULL);
+}
+
+static void
+checksched(void)
+{
+ /* Do nothing if WATCH is not set, or LOGCHECK has not elapsed */
+ if (watch && (int) difftime(time(NULL), lastwatch) > getiparam("LOGCHECK"))
+ dowatch();
}
/**/
-int
+static int
bin_log(UNUSED(char *nam), UNUSED(char **argv), UNUSED(Options ops), UNUSED(int func))
{
if (!watch)
@@ -611,16 +651,109 @@ bin_log(UNUSED(char *nam), UNUSED(char **argv), UNUSED(Options ops), UNUSED(int
#else /* !WATCH_STRUCT_UTMP */
-/**/
-void dowatch(void)
+static void
+checksched(void)
{
}
/**/
-int
+static int
bin_log(char *nam, char **argv, Options ops, int func)
{
return bin_notavail(nam, argv, ops, func);
}
#endif /* !WATCH_STRUCT_UTMP */
+
+/**/
+static char **watch; /* $watch */
+
+/* module setup */
+
+static struct builtin bintab[] = {
+ BUILTIN("log", 0, bin_log, 0, 0, 0, NULL, NULL),
+};
+
+static struct paramdef partab[] = {
+ PARAMDEF("WATCH", PM_SCALAR|PM_SPECIAL, &watch, NULL),
+ PARAMDEF("watch", PM_ARRAY|PM_SPECIAL, &watch, NULL),
+};
+
+static struct features module_features = {
+ bintab, sizeof(bintab)/sizeof(*bintab),
+ NULL, 0,
+ NULL, 0,
+ partab, sizeof(partab)/sizeof(*partab),
+ 0
+};
+
+/**/
+int
+setup_(UNUSED(Module m))
+{
+ /* On Cygwin, colonarr_gsu exists in libzsh.dll and we can't
+ * use &colonarr_gsu in the initialization of partab[] above */
+ partab[0].gsu = (void *)&colonarr_gsu;
+ partab[1].gsu = (void *)&vararray_gsu;
+ 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))
+{
+ static char const * const default_watchfmt = DEFAULT_WATCHFMT;
+
+ Param pma = (Param) paramtab->getnode(paramtab, "watch");
+ Param pms = (Param) paramtab->getnode(paramtab, "WATCH");
+ if (pma && pms && pma->u.arr == watch && pms->u.arr == watch) {
+ /* only tie the two parameters if both were added */
+ pma->ename = "WATCH";
+ pms->ename = "watch";
+ pma->node.flags |= PM_TIED;
+ pms->node.flags |= PM_TIED;
+ }
+ watch = mkarray(NULL);
+
+ /* These two parameters are only set to defaults if not set.
+ * So setting them in .zshrc will not be enough to load the
+ * module. It's useless until the watch array is set anyway. */
+ if (!paramtab->getnode(paramtab, "WATCHFMT"))
+ setsparam("WATCHFMT", ztrdup_metafy(default_watchfmt));
+ if (!paramtab->getnode(paramtab, "LOGCHECK"))
+ setiparam("LOGCHECK", 60);
+
+ addprepromptfn(&checksched);
+
+ return 0;
+}
+
+/**/
+int
+cleanup_(Module m)
+{
+ delprepromptfn(&checksched);
+ return setfeatureenables(m, &module_features, NULL);
+}
+
+/**/
+int
+finish_(UNUSED(Module m))
+{
+ return 0;
+}
diff --git a/Src/Modules/watch.mdd b/Src/Modules/watch.mdd
new file mode 100644
index 000000000..7e8454ede
--- /dev/null
+++ b/Src/Modules/watch.mdd
@@ -0,0 +1,7 @@
+name=zsh/watch
+link=dynamic
+load=yes
+
+autofeatures="b:log p:WATCH p:watch"
+
+objects="watch.o"
diff --git a/Src/Modules/zprof.c b/Src/Modules/zprof.c
index bc97771c0..56cdab888 100644
--- a/Src/Modules/zprof.c
+++ b/Src/Modules/zprof.c
@@ -213,7 +213,25 @@ bin_zprof(UNUSED(char *nam), UNUSED(char **args), Options ops, UNUSED(int func))
return 0;
}
-/**/
+static char *
+name_for_anonymous_function(char *name)
+{
+ char lineno[DIGBUFSIZE];
+ char *parts[7];
+
+ convbase(lineno, funcstack[0].flineno, 10);
+
+ parts[0] = name;
+ parts[1] = " [";
+ parts[2] = funcstack[0].filename ? funcstack[0].filename : "";
+ parts[3] = ":";
+ parts[4] = lineno;
+ parts[5] = "]";
+ parts[6] = NULL;
+
+ return sepjoin(parts, "", 1);
+}
+
static int
zprof_wrapper(Eprog prog, FuncWrap w, char *name)
{
@@ -224,12 +242,19 @@ zprof_wrapper(Eprog prog, FuncWrap w, char *name)
struct timeval tv;
struct timezone dummy;
double prev = 0, now;
+ char *name_for_lookups;
+
+ if (is_anonymous_function_name(name)) {
+ name_for_lookups = name_for_anonymous_function(name);
+ } else {
+ name_for_lookups = name;
+ }
if (zprof_module && !(zprof_module->node.flags & MOD_UNLOAD)) {
active = 1;
- if (!(f = findpfunc(name))) {
+ if (!(f = findpfunc(name_for_lookups))) {
f = (Pfunc) zalloc(sizeof(*f));
- f->name = ztrdup(name);
+ f->name = ztrdup(name_for_lookups);
f->calls = 0;
f->time = f->self = 0.0;
f->next = calls;
diff --git a/Src/Modules/zpty.c b/Src/Modules/zpty.c
index 45fd15ee0..dfd2a2a7a 100644
--- a/Src/Modules/zpty.c
+++ b/Src/Modules/zpty.c
@@ -30,6 +30,13 @@
#include "zpty.mdh"
#include "zpty.pro"
+#ifdef __CYGWIN__
+#include <cygwin/version.h>
+#if defined(CYGWIN_VERSION_DLL_MAJOR) && CYGWIN_VERSION_DLL_MAJOR<3002
+#define USE_CYGWIN_FIX 1
+#endif
+#endif
+
/* The number of bytes we normally read when given no pattern and the
* upper bound on the number of bytes we read (even if we are give a
* pattern). */
@@ -428,6 +435,7 @@ newptycmd(char *nam, char *pname, char **args, int echo, int nblock)
mypid = 0; /* trick to ensure we _exit() */
zexit(lastval, ZEXIT_NORMAL);
}
+#ifndef USE_CYGWIN_FIX
master = movefd(master);
if (master == -1) {
zerrnam(nam, "cannot duplicate fd %d: %e", master, errno);
@@ -435,6 +443,9 @@ newptycmd(char *nam, char *pname, char **args, int echo, int nblock)
ineval = oineval;
return 1;
}
+#else
+ addmodulefd(master, FDT_INTERNAL);
+#endif
p = (Ptycmd) zalloc(sizeof(*p));
diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c
index 24659cb16..2f17c03f1 100644
--- a/Src/Modules/zutil.c
+++ b/Src/Modules/zutil.c
@@ -96,7 +96,7 @@ struct stypat {
Stypat next;
char *pat; /* pattern string */
Patprog prog; /* compiled pattern */
- int weight; /* how specific is the pattern? */
+ zulong weight; /* how specific is the pattern? */
Eprog eval; /* eval-on-retrieve? */
char **vals;
};
@@ -200,7 +200,8 @@ printstylenode(HashNode hn, int printflags)
else {
printf("zstyle %s", (p->eval ? "-e " : ""));
quotedzputs(p->pat, stdout);
- printf(" %s", s->node.nam);
+ putchar(' ');
+ quotedzputs(s->node.nam, stdout);
}
for (v = p->vals; *v; v++) {
putchar(' ');
@@ -293,7 +294,9 @@ newzstyletable(int size, char const *name)
static int
setstypat(Style s, char *pat, Patprog prog, char **vals, int eval)
{
- int weight, tmp, first;
+ zulong weight;
+ int tmp;
+ int first;
char *str;
Stypat p, q, qq;
Eprog eprog = NULL;
@@ -348,6 +351,12 @@ setstypat(Style s, char *pat, Patprog prog, char **vals, int eval)
* - A component that's a literal string scores 2 points.
* - The score of a pattern is the sum of the score of its components.
*
+ * The result of this calculation is stored in the low bits of 'weight'.
+ * The high bits of 'weight' are used to store the number of ':'-separated
+ * components. This provides a lexicographic comparison: first compare
+ * the number of components, and if that's equal, compare the specificity
+ * of the components.
+ *
* This corresponds to the notion of 'more specific' in the zshmodules(1)
* documentation of zstyle.
*/
@@ -367,6 +376,7 @@ setstypat(Style s, char *pat, Patprog prog, char **vals, int eval)
if (*str == ':') {
/* Yet another component. */
+ weight += ZLONG_CONST(1) << (CHAR_BIT * sizeof(weight) / 2);
first = 1;
weight += tmp;
@@ -766,10 +776,12 @@ bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
* ousedp (*outp)[*ousedp] is where to write next
* olenp *olenp is the size allocated for *outp
* endchar Terminator character in addition to `\0' (may be '\0')
+ * presence -F: Ternary expressions test emptyness instead
* skip If 1, don't output, just parse.
*/
static char *zformat_substring(char* instr, char **specs, char **outp,
- int *ousedp, int *olenp, int endchar, int skip)
+ int *ousedp, int *olenp, int endchar,
+ int presence, int skip)
{
char *s;
@@ -797,27 +809,35 @@ 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';
- }
- else if (testit)
+ } else if (*s == '.' || testit)
s++;
if (testit && STOUC(*s)) {
int actval, testval, endcharl;
- /*
- * One one number is useful for ternary expressions.
- * Remember to put the sign back.
- */
+ /* Only one number is useful for ternary expressions. */
testval = (min >= 0) ? min : (max >= 0) ? max : 0;
- if (right)
- testval *= -1;
- if (specs[STOUC(*s)])
- actval = (int)mathevali(specs[STOUC(*s)]);
- else
- actval = 0;
- /* zero means values are equal, i.e. true */
- actval -= testval;
+ if (specs[STOUC(*s)] && *specs[STOUC(*s)]) {
+ if (presence) {
+ if (testval)
+#ifdef MULTIBYTE_SUPPORT
+ if (isset(MULTIBYTE))
+ actval = MB_METASTRWIDTH(specs[STOUC(*s)]);
+ else
+#endif
+ actval = strlen(specs[STOUC(*s)]);
+ else
+ actval = 1;
+ actval = right ? (testval < actval) : (testval >= actval);
+ } else {
+ if (right) /* put the sign back */
+ testval *= -1;
+ /* zero means values are equal, i.e. true */
+ actval = (int)mathevali(specs[STOUC(*s)]) - testval;
+ }
+ } else
+ actval = presence ? !right : testval;
/* careful about premature end of string */
if (!(endcharl = *++s))
@@ -828,10 +848,10 @@ static char *zformat_substring(char* instr, char **specs, char **outp,
* vice versa... unless we are already skipping.
*/
if (!(s = zformat_substring(s+1, specs, outp, ousedp,
- olenp, endcharl, skip || actval)))
+ olenp, endcharl, presence, skip || actval)))
return NULL;
if (!(s = zformat_substring(s+1, specs, outp, ousedp,
- olenp, ')', skip || !actval)))
+ olenp, ')', presence, skip || !actval)))
return NULL;
} else if (skip) {
continue;
@@ -903,6 +923,7 @@ static int
bin_zformat(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
{
char opt;
+ int presence = 0;
if (args[0][0] != '-' || !(opt = args[0][1]) || args[0][2]) {
zwarnnam(nam, "invalid argument: %s", args[0]);
@@ -911,15 +932,18 @@ bin_zformat(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
args++;
switch (opt) {
+ case 'F':
+ presence = 1;
+ /* fall-through */
case 'f':
{
- char **ap, *specs[256], *out;
+ char **ap, *specs[256] = {0}, *out;
int olen, oused = 0;
- memset(specs, 0, 256 * sizeof(char *));
-
specs['%'] = "%";
specs[')'] = ")";
+
+ /* Parse the specs in argv. */
for (ap = args + 2; *ap; ap++) {
if (!ap[0][0] || ap[0][0] == '-' || ap[0][0] == '.' ||
idigit(ap[0][0]) || ap[0][1] != ':') {
@@ -930,7 +954,8 @@ bin_zformat(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
}
out = (char *) zhalloc(olen = 128);
- zformat_substring(args[1], specs, &out, &oused, &olen, '\0', 0);
+ zformat_substring(args[1], specs, &out, &oused, &olen, '\0',
+ presence, 0);
out[oused] = '\0';
setsparam(args[0], ztrdup(out));
@@ -1865,7 +1890,10 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
while (*++o) {
if (!(d = sopts[STOUC(*o)])) {
if (fail) {
- zwarnnam(nam, "bad option: %c", *o);
+ if (*o != '-')
+ zwarnnam(nam, "bad option: -%c", *o);
+ else
+ zwarnnam(nam, "bad option: -%s", o);
return 1;
}
o = NULL;
@@ -1878,7 +1906,7 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
} else if (!(d->flags & ZOF_OPT) ||
(pp[1] && pp[1][0] != '-')) {
if (!pp[1]) {
- zwarnnam(nam, "missing argument for option: %s",
+ zwarnnam(nam, "missing argument for option: -%s",
d->name);
return 1;
}
@@ -1905,7 +1933,7 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
else if (!(d->flags & ZOF_OPT) ||
(pp[1] && pp[1][0] != '-')) {
if (!pp[1]) {
- zwarnnam(nam, "missing argument for option: %s",
+ zwarnnam(nam, "missing argument for option: -%s",
d->name);
return 1;
}
diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h
index 2e3249b52..2ca779fe5 100644
--- a/Src/Zle/comp.h
+++ b/Src/Zle/comp.h
@@ -85,8 +85,8 @@ struct cmgroup {
#define CGF_NOSORT 1 /* don't sort this group */
#define CGF_LINES 2 /* these are to be printed on different lines */
#define CGF_HASDL 4 /* has display strings printed on separate lines */
-#define CGF_UNIQALL 8 /* remove all duplicates */
-#define CGF_UNIQCON 16 /* remove consecutive duplicates */
+#define CGF_UNIQALL 8 /* remove consecutive duplicates (if neither are set, */
+#define CGF_UNIQCON 16 /* don't deduplicate */ /* remove all dupes) */
#define CGF_PACKED 32 /* LIST_PACKED for this group */
#define CGF_ROWS 64 /* LIST_ROWS_FIRST for this group */
#define CGF_FILES 128 /* contains file names */
@@ -140,6 +140,7 @@ struct cmatch {
#define CMF_ALL (1<<13) /* a match representing all other matches */
#define CMF_DUMMY (1<<14) /* unselectable dummy match */
#define CMF_MORDER (1<<15) /* order by matches, not display strings */
+#define CMF_DELETE (1<<16) /* used for deduplication of unsorted matches, don't set */
/* Stuff for completion matcher control. */
@@ -299,7 +300,7 @@ struct menuinfo {
#define CAF_NOSORT 2 /* compadd -V: don't sort */
#define CAF_MATCH 4 /* compadd without -U: do matching */
#define CAF_UNIQCON 8 /* compadd -2: don't deduplicate */
-#define CAF_UNIQALL 16 /* compadd -1: deduplicate */
+#define CAF_UNIQALL 16 /* compadd -1: deduplicate consecutive only */
#define CAF_ARRAYS 32 /* compadd -a or -k: array/assoc parameter names */
#define CAF_KEYS 64 /* compadd -k: assoc parameter names */
#define CAF_ALL 128 /* compadd -C: _all_matches */
@@ -329,7 +330,7 @@ struct cadata {
char *exp; /* explanation (-X) */
char *apar; /* array to store matches in (-A) */
char *opar; /* array to store originals in (-O) */
- char *dpar; /* array to delete non-matches in (-D) */
+ char **dpar; /* arrays to delete non-matches in (-D) */
char *disp; /* array with display lists (-d) */
char *mesg; /* message to show unconditionally (-x) */
int dummies; /* add that many dummy matches */
diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c
index 7e3badc57..fe3ea10fc 100644
--- a/Src/Zle/compcore.c
+++ b/Src/Zle/compcore.c
@@ -648,7 +648,7 @@ callcompfunc(char *s, char *fn)
if (compredirs)
freearray(compredirs);
if (rdstrs)
- compredirs = zlinklist2array(rdstrs);
+ compredirs = zlinklist2array(rdstrs, 1);
else
compredirs = (char **) zshcalloc(sizeof(char *));
@@ -821,6 +821,7 @@ callcompfunc(char *s, char *fn)
sfcontext = SFC_CWIDGET;
NEWHEAPS(compheap) {
LinkList largs = NULL;
+ int oxt = isset(XTRACE);
if (*cfargs) {
char **p = cfargs;
@@ -830,7 +831,9 @@ callcompfunc(char *s, char *fn)
while (*p)
addlinknode(largs, dupstring(*p++));
}
+ opts[XTRACE] = 0;
cfret = doshfunc(shfunc, largs, 1);
+ opts[XTRACE] = oxt;
} OLDHEAPS;
sfcontext = osc;
endparamscope();
@@ -1122,6 +1125,18 @@ check_param(char *s, int set, int test)
*
* TODO: passing s as a parameter while we get some mysterious
* offset "offs" into it via a global sucks badly.
+ *
+ * From ../lex.c we know:
+ * wb is the beginning position of the current word in the line
+ * we is the position of the end of the current word in the line
+ * From zle_tricky.c we know:
+ * offs is position within the word where we are completing
+ *
+ * So wb + offs is the current cursor position if COMPLETE_IN_WORD
+ * is set, otherwise it is the end of the word (same as we).
+ *
+ * Note below we are thus stepping backward from our completion
+ * position to find a '$' in the current word (if any).
*/
for (p = s + offs; ; p--) {
if (*p == String || *p == Qstring) {
@@ -1168,13 +1183,13 @@ check_param(char *s, int set, int test)
char *tb = b;
/* If this is a ${...}, see if we are before the '}'. */
- if (!skipparens(Inbrace, Outbrace, &tb))
+ if (!skipparens(Inbrace, Outbrace, &tb) && tb - s <= offs)
return NULL;
/* Ignore the possible (...) flags. */
b++, br++;
if ((qstring ? skipparens('(', ')', &b) :
- skipparens(Inpar, Outpar, &b)) > 0) {
+ skipparens(Inpar, Outpar, &b)) > 0 || b - s > offs) {
/*
* We are still within the parameter flags. There's no
* point trying to do anything clever here with
@@ -1922,7 +1937,7 @@ set_comp_sep(void)
mod_export void
set_list_array(char *name, LinkList l)
{
- setaparam(name, zlinklist2array(l));
+ setaparam(name, zlinklist2array(l, 1));
}
/* Get the words from a variable or a (list of words). */
@@ -2058,10 +2073,10 @@ addmatches(Cadata dat, char **argv)
/* ms: "match string" - string to use as completion.
* Overloaded at one place as a temporary. */
char *s, *ms, *lipre = NULL, *lisuf = NULL, *lpre = NULL, *lsuf = NULL;
- char **aign = NULL, **dparr = NULL, *oaq = autoq, *oppre = dat->ppre;
+ char **aign = NULL, ***dparr = NULL, *oaq = autoq, *oppre = dat->ppre;
char *oqp = qipre, *oqs = qisuf, qc, **disp = NULL, *ibuf = NULL;
char **arrays = NULL;
- int lpl, lsl, bcp = 0, bcs = 0, bpadd = 0, bsadd = 0;
+ int dind, lpl, lsl, bcp = 0, bcs = 0, bpadd = 0, bsadd = 0;
int ppl = 0, psl = 0, ilen = 0;
int llpl = 0, llsl = 0, nm = mnum, gflags = 0, ohp = haspattern;
int isexact, doadd, ois = instring, oib = inbackt;
@@ -2069,7 +2084,7 @@ addmatches(Cadata dat, char **argv)
struct cmlist mst;
Cmlist oms = mstack;
Patprog cp = NULL, *pign = NULL;
- LinkList aparl = NULL, oparl = NULL, dparl = NULL;
+ LinkList aparl = NULL, oparl = NULL, *dparl = NULL;
Brinfo bp, bpl = brbeg, obpl, bsl = brend, obsl;
Heap oldheap;
@@ -2097,7 +2112,7 @@ addmatches(Cadata dat, char **argv)
curexpl->always = !!dat->mesg;
curexpl->count = curexpl->fcount = 0;
curexpl->str = dupstring(dat->mesg ? dat->mesg : dat->exp);
- if (dat->mesg)
+ if (dat->mesg && !dat->dpar && !dat->opar && !dat->apar)
addexpl(1);
} else
curexpl = NULL;
@@ -2163,11 +2178,24 @@ addmatches(Cadata dat, char **argv)
if (dat->opar)
oparl = newlinklist();
if (dat->dpar) {
- if (*(dat->dpar) == '(')
- dparr = NULL;
- else if ((dparr = get_user_var(dat->dpar)) && !*dparr)
- dparr = NULL;
- dparl = newlinklist();
+ int darr = 0, dparlen = arrlen(dat->dpar);
+ char **tail = dat->dpar + dparlen;
+
+ dparr = (char ***)hcalloc((1 + dparlen) * sizeof(char **));
+ dparl = (LinkList *)hcalloc((1 + dparlen) * sizeof(LinkList));
+ queue_signals();
+ while (darr < dparlen) {
+ if ((dparr[darr] = getaparam(dat->dpar[darr])) && *dparr[darr]) {
+ dparr[darr] = arrdup(dparr[darr]);
+ dparl[darr++] = newlinklist();
+ } else {
+ /* swap in the last -D argument if we didn't get a non-empty array */
+ dat->dpar[darr] = *--tail;
+ *tail = NULL;
+ --dparlen;
+ }
+ }
+ unqueue_signals();
}
/* Store the matcher in our stack of matchers. */
if (dat->match) {
@@ -2484,8 +2512,10 @@ addmatches(Cadata dat, char **argv)
}
if (!addit) {
compignored++;
- if (dparr && !*++dparr)
- dparr = NULL;
+ for (dind = 0; dparl && dparl[dind]; dind++) {
+ if (dparr[dind] && !*++dparr[dind])
+ dparr[dind] = NULL;
+ }
goto next_array;
}
}
@@ -2502,8 +2532,10 @@ addmatches(Cadata dat, char **argv)
!(dat->flags & CMF_FILE) ? 1 : 2) : 0),
&bpl, bcp, &bsl, bcs,
&isexact))) {
- if (dparr && !*++dparr)
- dparr = NULL;
+ for (dind = 0; dparl && dparl[dind]; dind++) {
+ if (dparr[dind] && !*++dparr[dind])
+ dparr[dind] = NULL;
+ }
goto next_array;
}
if (doadd) {
@@ -2530,10 +2562,14 @@ addmatches(Cadata dat, char **argv)
addlinknode(aparl, ms);
if (dat->opar)
addlinknode(oparl, s);
- if (dat->dpar && dparr) {
- addlinknode(dparl, *dparr);
- if (!*++dparr)
- dparr = NULL;
+ if (dat->dpar) {
+ for (dind = 0; dparl[dind]; dind++) {
+ if (dparr[dind]) {
+ addlinknode(dparl[dind], *dparr[dind]);
+ if (!*++dparr[dind])
+ dparr[dind] = NULL;
+ }
+ }
}
free_cline(lc);
}
@@ -2561,8 +2597,10 @@ addmatches(Cadata dat, char **argv)
set_list_array(dat->apar, aparl);
if (dat->opar)
set_list_array(dat->opar, oparl);
- if (dat->dpar)
- set_list_array(dat->dpar, dparl);
+ if (dat->dpar) {
+ for (dind = 0; dparl[dind]; dind++)
+ set_list_array(dat->dpar[dind], dparl[dind]);
+ }
if (dat->exp)
addexpl(0);
if (!hasallmatch && (dat->aflags & CAF_ALL)) {
@@ -3209,17 +3247,20 @@ makearray(LinkList l, int type, int flags, int *np, int *nlp, int *llp)
}
*cp = NULL;
}
- } else {
+ } else if (n > 0) {
if (!(flags & CGF_NOSORT)) {
/* Now sort the array (it contains matches). */
matchorder = flags;
qsort((void *) rp, n, sizeof(Cmatch),
(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,
+ * so this condition only checks for -2 */
if (!(flags & CGF_UNIQCON)) {
int dup;
- /* And delete the ones that occur more than once. */
+ /* we did not pass -2 so go ahead and remove those dupes */
for (ap = cp = rp; *ap; ap++) {
*cp++ = *ap;
for (bp = ap; bp[1] && matcheq(*ap, bp[1]); bp++, n--);
@@ -3241,30 +3282,47 @@ makearray(LinkList l, int type, int flags, int *np, int *nlp, int *llp)
if ((*ap)->flags & (CMF_NOLIST | CMF_MULT))
nl++;
}
+ /* used -O nosort or -V, don't sort */
} else {
+ /* didn't use -1 or -2, so remove all duplicates (efficient) */
if (!(flags & CGF_UNIQALL) && !(flags & CGF_UNIQCON)) {
- int dup;
-
- for (ap = rp; *ap; ap++) {
- for (bp = cp = ap + 1; *bp; bp++) {
- if (!matcheq(*ap, *bp))
- *cp++ = *bp;
- else
+ int dup, del = 0;
+
+ /* To avoid O(n^2) here, sort a copy of the list, then remove marked elements */
+ matchorder = flags;
+ Cmatch *sp, *asp;
+ 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);
+ for (asp = sp + 1; *asp; asp++) {
+ Cmatch *ap = asp - 1, *bp = asp;
+ if (matcheq(*ap, *bp)) {
+ bp[0]->flags = CMF_DELETE;
+ del = 1;
+ } else if (!ap[0]->disp) {
+ /* Mark those, that would show the same string in the list. */
+ for (dup = 0; bp[0] && !(bp[0])->disp &&
+ !strcmp((*ap)->str, (bp[0])->str); bp = ++sp) {
+ (bp[0])->flags |= CMF_MULT;
+ dup = 1;
+ }
+ if (dup)
+ (*ap)->flags |= CMF_FMULT;
+ }
+ }
+ if (del) {
+ int n_orig = n;
+ for (bp = rp, ap = rp; bp < rp + n_orig; ap++, bp++) {
+ while (bp[0]->flags & CMF_DELETE) {
+ bp++;
n--;
+ }
+ *ap = *bp;
}
- *cp = NULL;
- if (!(*ap)->disp) {
- for (dup = 0, bp = ap + 1; *bp; bp++)
- if (!(*bp)->disp &&
- !((*bp)->flags & CMF_MULT) &&
- !strcmp((*ap)->str, (*bp)->str)) {
- (*bp)->flags |= CMF_MULT;
- dup = 1;
- }
- if (dup)
- (*ap)->flags |= CMF_FMULT;
- }
+ *ap = NULL;
}
+ /* passed -1 but not -2, so remove consecutive duplicates (efficient) */
} else if (!(flags & CGF_UNIQCON)) {
int dup;
diff --git a/Src/Zle/complete.c b/Src/Zle/complete.c
index 7beb6d847..67a60963e 100644
--- a/Src/Zle/complete.c
+++ b/Src/Zle/complete.c
@@ -607,6 +607,7 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
char *oarg = NULL; /* argument of -o option */
int added; /* return value */
Cmatcher match = NULL;
+ size_t dparlen = 0, dparsize = 0; /* no. of -D options and array size */
if (incompfunc != 1) {
zwarnnam(name, "can only be called from completion function");
@@ -614,7 +615,8 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
}
dat.ipre = dat.isuf = dat.ppre = dat.psuf = dat.prpre = dat.mesg =
dat.pre = dat.suf = dat.group = dat.rems = dat.remf = dat.disp =
- dat.ign = dat.exp = dat.apar = dat.opar = dat.dpar = NULL;
+ dat.ign = dat.exp = dat.apar = dat.opar = NULL;
+ dat.dpar = NULL;
dat.match = NULL;
dat.flags = 0;
dat.aflags = CAF_MATCH;
@@ -741,7 +743,12 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
e = "parameter name expected after -%c";
break;
case 'D':
- sp = &(dat.dpar);
+ if (dparsize <= dparlen + 1) {
+ dparsize = (dparsize + 1) * 2;
+ dat.dpar = (char **)zrealloc(dat.dpar, sizeof(char *) * dparsize);
+ }
+ sp = dat.dpar + dparlen++;
+ *sp = dat.dpar[dparlen] = NULL;
e = "parameter name expected after -%c";
break;
case 'd':
@@ -768,11 +775,13 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
} else {
zwarnnam(name, "number expected after -%c", *p);
zsfree(mstr);
+ zfree(dat.dpar, dparsize);
return 1;
}
if (dat.dummies < 0) {
zwarnnam(name, "invalid number: %d", dat.dummies);
zsfree(mstr);
+ zfree(dat.dpar, dparsize);
return 1;
}
break;
@@ -782,6 +791,7 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
default:
zwarnnam(name, "bad option: -%c", *p);
zsfree(mstr);
+ zfree(dat.dpar, dparsize);
return 1;
}
if (sp) {
@@ -802,6 +812,7 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
/* Missing argument: argv[N] == "-X", argv[N+1] == NULL. */
zwarnnam(name, e, *p);
zsfree(mstr);
+ zfree(dat.dpar, dparsize);
return 1;
}
if (m) {
@@ -820,17 +831,21 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
if (mstr && (match = parse_cmatcher(name, mstr)) == pcm_err) {
zsfree(mstr);
+ zfree(dat.dpar, dparsize);
return 1;
}
zsfree(mstr);
if (!*argv && !dat.group && !dat.mesg &&
- !(dat.aflags & (CAF_NOSORT|CAF_UNIQALL|CAF_UNIQCON|CAF_ALL)))
+ !(dat.aflags & (CAF_NOSORT|CAF_UNIQALL|CAF_UNIQCON|CAF_ALL))) {
+ zfree(dat.dpar, dparsize);
return 1;
+ }
dat.match = match = cpcmatcher(match);
added = addmatches(&dat, argv);
freecmatcher(match);
+ zfree(dat.dpar, dparsize);
return added;
}
@@ -1343,7 +1358,7 @@ get_compstate(Param pm)
/**/
static void
-set_compstate(UNUSED(Param pm), HashTable ht)
+set_compstate(Param pm, HashTable ht)
{
struct compparam *cp;
Param *pp;
diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c
index 429c8159f..0dc64db6a 100644
--- a/Src/Zle/complist.c
+++ b/Src/Zle/complist.c
@@ -603,6 +603,16 @@ zcoff(void)
zcputs(NULL, COL_NO);
}
+/* Clear to end of line, if possible and necessary. */
+static void
+cleareol()
+{
+ if (mlbeg >= 0 && tccan(TCCLEAREOL)) {
+ if (*last_cap)
+ zcoff(); /* If we used colors, prevent them from bleeding. */
+ tcout(TCCLEAREOL);
+ }
+}
static void
initiscol(void)
@@ -670,8 +680,7 @@ clprintfmt(char *p, int ml)
doiscol(i++);
cc++;
if (*p == '\n') {
- if (mlbeg >= 0 && tccan(TCCLEAREOL))
- tcout(TCCLEAREOL);
+ cleareol();
cc = 0;
}
if (ml == mlend - 1 && (cc % zterm_columns) == zterm_columns - 1)
@@ -693,8 +702,7 @@ clprintfmt(char *p, int ml)
!--mrestlines && (ask = asklistscroll(ml)))
return ask;
}
- if (mlbeg >= 0 && tccan(TCCLEAREOL))
- tcout(TCCLEAREOL);
+ cleareol();
return 0;
}
@@ -1047,8 +1055,7 @@ compprintnl(int ml)
{
int ask;
- if (mlbeg >= 0 && tccan(TCCLEAREOL))
- tcout(TCCLEAREOL);
+ cleareol();
putc('\n', shout);
if (mscroll && !--mrestlines && (ask = asklistscroll(ml)))
@@ -1263,8 +1270,8 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop)
if ((cc >= zterm_columns - 2 || cchar == ZWC('\n')) && stat)
dopr = 2;
if (cchar == ZWC('\n')) {
- if (dopr == 1 && mlbeg >= 0 && tccan(TCCLEAREOL))
- tcout(TCCLEAREOL);
+ if (dopr == 1)
+ cleareol();
l += 1 + ((cc - 1) / zterm_columns);
cc = 0;
}
@@ -1306,8 +1313,7 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop)
if (dopr) {
if (!(cc % zterm_columns))
fputs(" \010", shout);
- if (mlbeg >= 0 && tccan(TCCLEAREOL))
- tcout(TCCLEAREOL);
+ cleareol();
}
if (stat && n)
mfirstl = -1;
@@ -1338,8 +1344,8 @@ compzputs(char const *s, int ml)
c = *s;
s++;
putc(c, shout);
- if (c == '\n' && mlbeg >= 0 && tccan(TCCLEAREOL))
- tcout(TCCLEAREOL);
+ if (c == '\n')
+ cleareol();
if (mscroll && (++col == zterm_columns || c == '\n')) {
ml++;
if (!--mrestlines && (ask = asklistscroll(ml)))
@@ -1692,8 +1698,7 @@ compprintlist(int showall)
lastlistlen = listdat.nlines;
} else if ((nl = listdat.nlines + nlnct - 1) < zterm_lines) {
- if (mlbeg >= 0 && tccan(TCCLEAREOL))
- tcout(TCCLEAREOL);
+ cleareol();
tcmultout(TCUP, TCMULTUP, nl);
showinglist = -1;
@@ -3277,9 +3282,8 @@ domenuselect(Hookdef dummy, Chdata dat)
!strcmp(cmd->nam, "reverse-menu-complete")) {
mode = 0;
comprecursive = 1;
- unmetafy_line();
- reversemenucomplete(zlenoargs);
- metafy_line();
+ zmult = -zmult;
+ do_menucmp(0);
mselect = (*(minfo.cur))->gnum;
setwish = 1;
mline = -1;
diff --git a/Src/Zle/compmatch.c b/Src/Zle/compmatch.c
index cc4c3eca9..bb8359f1d 100644
--- a/Src/Zle/compmatch.c
+++ b/Src/Zle/compmatch.c
@@ -693,8 +693,9 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp,
alen = mp->ralen; aol = mp->lalen;
}
/* Give up if we don't have enough characters for the
- * line-string and the anchor. */
- if (ll < llen + alen || lw < alen)
+ * line-string and the anchor, or for both anchors in
+ * the case of the trial completion word. */
+ if (ll < llen + alen || lw < alen + aol)
continue;
if (mp->flags & CMF_LEFT) {
@@ -1318,7 +1319,7 @@ pattern_match_equivalence(Cpattern lp, convchar_t wind, int wmtp,
convchar_t lchr;
int lmtp;
- if (!PATMATCHINDEX(lp->u.str, wind-1, &lchr, &lmtp)) {
+ if (!PATMATCHINDEX(lp->u.str, wind, &lchr, &lmtp)) {
/*
* No equivalent. No possible match; give up.
*/
@@ -1437,7 +1438,7 @@ pattern_match_restrict(Cpattern p, Cpattern wp, convchar_t *wsc, int wsclen,
/*
* If either is "?", they match each other; no further tests.
- * Apply this even if the character wasn't convertable;
+ * Apply this even if the character wasn't convertible;
* there's no point trying to be clever in that case.
*/
if (p->tp != CPAT_ANY || wp->tp != CPAT_ANY)
@@ -1495,7 +1496,7 @@ pattern_match_restrict(Cpattern p, Cpattern wp, convchar_t *wsc, int wsclen,
* characters. We're matching two patterns against
* one another to generate a character to insert.
* This is a bit too psychedelic, so I'm going to
- * bale out now. See you on the ground.
+ * bail out now. See you on the ground.
*/
return 0;
}
@@ -1563,7 +1564,7 @@ pattern_match(Cpattern p, char *s, Cpattern wp, char *ws)
c = unmeta_one(s, &len);
/*
* If either is "?", they match each other; no further tests.
- * Apply this even if the character wasn't convertable;
+ * Apply this even if the character wasn't convertible;
* there's no point trying to be clever in that case.
*/
if (p->tp != CPAT_ANY || wp->tp != CPAT_ANY)
@@ -1933,7 +1934,7 @@ bld_line(Cmatcher mp, ZLE_STRING_T line, char *mword, char *word,
* This is the nightmare case: we have line and
* and word matchers and some pattern which restricts
* the value on the line without us knowing exactly
- * what it is. Despatch to the special function
+ * what it is. Dispatch to the special function
* for that.
*/
if (mp && !mp->flags && mp->wlen <= wlen &&
diff --git a/Src/Zle/compresult.c b/Src/Zle/compresult.c
index 30fc60b78..57789c0f3 100644
--- a/Src/Zle/compresult.c
+++ b/Src/Zle/compresult.c
@@ -612,9 +612,10 @@ instmatch(Cmatch m, int *scs)
int pcs = zlemetacs;
l = 0;
- for (bp = brbeg, brpos = m->brpl,
- bradd = (m->pre ? strlen(m->pre) : 0);
- bp; bp = bp->next, brpos++) {
+ bradd = (m->pre ? strlen(m->pre) : 0);
+ for (bp = brbeg, brpos = m->brpl;
+ bp && brpos;
+ bp = bp->next, brpos++) {
zlemetacs = a + *brpos + bradd;
pcs = zlemetacs;
l = strlen(bp->str);
@@ -1583,7 +1584,7 @@ calclist(int showall)
nlines += 1 + printfmt(m->disp, 0, 0, 0);
g->flags |= CGF_HASDL;
} else {
- l = ZMB_nicewidth(m->disp);
+ l = ZMB_nicewidth(m->disp) + !!m->modec;
ndisp++;
if (l > glong)
glong = l;
@@ -2247,15 +2248,13 @@ iprintm(Cmgroup g, Cmatch *mp, UNUSED(int mc), UNUSED(int ml), int lastc, int wi
#ifdef MULTIBYTE_SUPPORT
len = mb_niceformat(m->disp, shout, NULL, 0);
#else
- nicezputs(m->disp, shout);
- len = niceztrlen(m->disp);
+ len = sb_niceformat(m->disp, shout, NULL, 0);
#endif
} else {
#ifdef MULTIBYTE_SUPPORT
len = mb_niceformat(m->str, shout, NULL, 0);
#else
- nicezputs(m->str, shout);
- len = niceztrlen(m->str);
+ len = sb_niceformat(m->str, shout, NULL, 0);
#endif
if ((g->flags & CGF_FILES) && m->modec) {
diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c
index 90db8b4b8..59abb4cc4 100644
--- a/Src/Zle/computil.c
+++ b/Src/Zle/computil.c
@@ -1035,7 +1035,7 @@ freecadef(Cadef d)
freecaargs(d->rest);
zsfree(d->nonarg);
if (d->single)
- zfree(d->single, 256 * sizeof(Caopt));
+ zfree(d->single, 188 * sizeof(Caopt));
zfree(d, sizeof(*d));
d = s;
}
@@ -1079,6 +1079,21 @@ bslashcolon(char *s)
return r;
}
+/* Get an index into the single array used in struct cadef
+ * opt is the option letter and pre is either - or +
+ * we only keep an array for the 94 ASCII characters from ! to ~ so
+ * with - and + prefixes, the array is double that at 188 elements
+ * returns -1 if opt is out-of-range
+ */
+static int
+single_index(char pre, char opt)
+{
+ if (opt <= 0x20 || opt > 0x7e)
+ return -1;
+
+ return opt + (pre == '-' ? -0x21 : 94 - 0x21);
+}
+
/* Parse an argument definition. */
static Caarg
@@ -1151,8 +1166,8 @@ alloc_cadef(char **args, int single, char *match, char *nonarg, int flags)
ret->lastt = time(0);
ret->set = NULL;
if (single) {
- ret->single = (Caopt *) zalloc(256 * sizeof(Caopt));
- memset(ret->single, 0, 256 * sizeof(Caopt));
+ ret->single = (Caopt *) zalloc(188 * sizeof(Caopt));
+ memset(ret->single, 0, 188 * sizeof(Caopt));
} else
ret->single = NULL;
ret->match = ztrdup(match);
@@ -1558,9 +1573,10 @@ parse_cadef(char *nam, char **args)
* pointer for the definition in the array for fast lookup.
* But don't treat '--' as a single option called '-' */
-
- if (single && name[1] && !name[2] && name[1] != '-')
- ret->single[STOUC(name[1])] = opt;
+ if (single && name[1] && !name[2] && name[1] != '-') {
+ int sidx = single_index(*name, name[1]);
+ if (sidx >= 0) ret->single[sidx] = opt;
+ }
if (again == 1) {
/* Do it all again for `*-...'. */
@@ -1738,11 +1754,12 @@ ca_get_sopt(Cadef d, char *line, char **end, LinkList *lp)
Caopt p, pp = NULL;
char pre = *line++;
LinkList l = NULL;
+ int sidx;
*lp = NULL;
for (p = NULL; *line; line++) {
- if ((p = d->single[STOUC(*line)]) && p->active &&
- p->args && p->name[0] == pre) {
+ if ((sidx = single_index(pre, *line)) != -1 &&
+ (p = d->single[sidx]) && p->active && p->args) {
if (p->type == CAO_NEXT) {
if (!l)
*lp = l = newlinklist();
@@ -1813,7 +1830,7 @@ ca_get_arg(Cadef d, int n)
/* Mark options as inactive.
* d: option definitions for a set
* pass either:
- * xor: a list if exclusions
+ * xor: a list of exclusions
* opts: if set, all options excluded leaving only nornal/rest arguments */
static void
@@ -2031,9 +2048,9 @@ ca_parse_line(Cadef d, Cadef all, int multi, int first)
state.def = state.ddef = NULL;
state.curopt = state.dopt = NULL;
state.argbeg = state.optbeg = state.nargbeg = state.restbeg = state.actopts =
- state.nth = state.inopt = state.inarg = state.opt = state.arg = 1;
+ state.nth = state.inarg = state.opt = state.arg = 1;
state.argend = argend = arrlen(compwords) - 1;
- state.singles = state.oopt = 0;
+ state.inopt = state.singles = state.oopt = 0;
state.curpos = compcurrent;
state.args = znewlinklist();
state.oargs = (LinkList *) zalloc(d->nopts * sizeof(LinkList));
@@ -2080,9 +2097,14 @@ ca_parse_line(Cadef d, Cadef all, int multi, int first)
remnulargs(line);
untokenize(line);
- ca_inactive(d, argxor, cur - 1, 0);
- if ((d->flags & CDF_SEP) && cur != compcurrent && !strcmp(line, "--")) {
+ if (argxor) {
+ ca_inactive(d, argxor, cur - 1, 0);
+ argxor = NULL;
+ }
+ if ((d->flags & CDF_SEP) && cur != compcurrent && state.actopts &&
+ !strcmp(line, "--")) {
ca_inactive(d, NULL, cur, 1);
+ state.actopts = 0;
continue;
}
@@ -2136,7 +2158,8 @@ ca_parse_line(Cadef d, Cadef all, int multi, int first)
/* See if it's an option. */
- if (state.opt == 2 && (state.curopt = ca_get_opt(d, line, 0, &pe)) &&
+ if (state.opt == 2 && (*line == '-' || *line == '+') &&
+ (state.curopt = ca_get_opt(d, line, 0, &pe)) &&
(state.curopt->type == CAO_OEQUAL ?
(compwords[cur] || pe[-1] == '=') :
(state.curopt->type == CAO_EQUAL ?
@@ -2184,12 +2207,14 @@ ca_parse_line(Cadef d, Cadef all, int multi, int first)
state.curopt = NULL;
}
} else if (state.opt == 2 && d->single &&
+ (*line == '-' || *line == '+') &&
((state.curopt = ca_get_sopt(d, line, &pe, &sopts)) ||
(cur != compcurrent && sopts && nonempty(sopts)))) {
/* Or maybe it's a single-letter option? */
char *p;
Caopt tmpopt;
+ int sidx;
if (cur != compcurrent && sopts && nonempty(sopts))
state.curopt = (Caopt) uremnode(sopts, firstnode(sopts));
@@ -2207,7 +2232,8 @@ ca_parse_line(Cadef d, Cadef all, int multi, int first)
state.singles = (!pe || !*pe);
for (p = line + 1; p < pe; p++) {
- if ((tmpopt = d->single[STOUC(*p)])) {
+ if ((sidx = single_index(*line, *p)) != -1 &&
+ (tmpopt = d->single[sidx])) {
if (!state.oargs[tmpopt->num])
state.oargs[tmpopt->num] = znewlinklist();
@@ -2235,11 +2261,17 @@ ca_parse_line(Cadef d, Cadef all, int multi, int first)
} else if (multi && (*line == '-' || *line == '+') && cur != compcurrent
&& (ca_foreign_opt(d, all, line)))
return 1;
- else if (state.arg &&
- (!napat || cur <= compcurrent || !pattry(napat, line))) {
+ else if (state.arg && cur <= compcurrent) {
/* Otherwise it's a normal argument. */
- if (napat && cur <= compcurrent)
+
+ /* test pattern passed to -A. if it matches treat this as an unknown
+ * option and continue to the next word */
+ if (napat && cur < compcurrent && state.actopts) {
+ if (pattry(napat, line))
+ continue;
ca_inactive(d, NULL, cur + 1, 1);
+ state.actopts = 0;
+ }
arglast = 1;
/* if this is the first normal arg after an option, may have been
@@ -2293,7 +2325,7 @@ ca_parse_line(Cadef d, Cadef all, int multi, int first)
if (adef)
state.oopt = adef->num - state.nth;
- if (state.def)
+ if (state.def && cur != compcurrent)
argxor = state.def->xor;
if (state.def && state.def->type != CAA_NORMAL &&
@@ -2375,6 +2407,23 @@ ca_parse_line(Cadef d, Cadef all, int multi, int first)
return 0;
}
+/* Build a NUL-separated from a list.
+ *
+ * This is only used to populate values of $opt_args.
+ */
+
+static char *
+ca_nullist(LinkList l)
+{
+ if (l) {
+ char **array = zlinklist2array(l, 0 /* don't dup elements */);
+ char *ret = zjoin(array, '\0', 0 /* permanent allocation */);
+ free(array); /* the elements are owned by the list */
+ return ret;
+ } else
+ return ztrdup("");
+}
+
/* Build a colon-list from a list.
*
* This is only used to populate values of $opt_args.
@@ -2563,7 +2612,7 @@ bin_comparguments(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
case 's': min = 1; max = 1; break;
case 'M': min = 1; max = 1; break;
case 'a': min = 0; max = 0; break;
- case 'W': min = 2; max = 2; break;
+ case 'W': min = 3; max = 3; break;
case 'n': min = 1; max = 1; break;
default:
zwarnnam(nam, "invalid option: %s", args[0]);
@@ -2795,11 +2844,20 @@ bin_comparguments(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
return 0;
return 1;
case 'W':
- /* This gets two parameter names as arguments. The first is set to
- * the current word sans any option prefixes handled by comparguments.
+ /* This gets two parameter names and one integer as arguments.
+ *
+ * The first parameter is set to the current word sans any option
+ * prefixes handled by comparguments.
+ *
* The second parameter is set to an array containing the options on
* the line and their arguments. I.e. the stuff _arguments returns
- * to its caller in the `line' and `opt_args' parameters. */
+ * to its caller in the `line' and `opt_args' parameters.
+ *
+ * The integer is one if the second parameter (which is just $opt_args,
+ * you know) should encode multiple values by joining them with NULs
+ * and zero if it should encode multiple values by joining them with
+ * colons after backslash-escaping colons and backslashes.
+ */
{
Castate s;
char **ret, **p;
@@ -2807,6 +2865,7 @@ bin_comparguments(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
LinkList *a;
Caopt o;
int num;
+ int opt_args_use_NUL_separators = (args[3][0] != '0');
for (num = 0, s = lstate; s; s = s->snext)
num += countlinknodes(s->args);
@@ -2832,7 +2891,10 @@ bin_comparguments(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
if (*a) {
*p++ = (o->gsname ? tricat(o->gsname, o->name, "") :
ztrdup(o->name));
- *p++ = ca_colonlist(*a);
+ if (opt_args_use_NUL_separators)
+ *p++ = ca_nullist(*a);
+ else
+ *p++ = ca_colonlist(*a);
}
*p = NULL;
@@ -3591,7 +3653,7 @@ bin_compvalues(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
if (cv_laststate.vals) {
char **ret;
- ret = zlinklist2array(cv_laststate.vals);
+ ret = zlinklist2array(cv_laststate.vals, 1);
sethparam(args[1], ret);
return 0;
@@ -4016,7 +4078,7 @@ bin_comptry(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
set = (Ctset) zalloc(sizeof(*set));
- set->tags = zlinklist2array(list);
+ set->tags = zlinklist2array(list, 1);
set->next = NULL;
set->ptr = NULL;
set->tag = NULL;
diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h
index 609493f8c..391586c4a 100644
--- a/Src/Zle/zle.h
+++ b/Src/Zle/zle.h
@@ -447,6 +447,10 @@ struct region_highlight {
* Any of the flags defined above.
*/
int flags;
+ /*
+ * User-settable "memo" key. Metafied.
+ */
+ const char *memo;
};
/*
diff --git a/Src/Zle/zle_hist.c b/Src/Zle/zle_hist.c
index 581ca4979..cfaa70dae 100644
--- a/Src/Zle/zle_hist.c
+++ b/Src/Zle/zle_hist.c
@@ -683,8 +683,13 @@ insertlastword(char **args)
}
nwords = countlinknodes(l);
} else {
- /* Some stored line. */
- if (!(he = quietgethist(evhist)) || !he->nwords) {
+ /* Some stored line. By default, search for a non-empty line. */
+ while ((he = quietgethist(evhist)) && histstep == -1 && !*args) {
+ if (he->nwords)
+ break;
+ evhist = addhistnum(evhist, histstep, HIST_FOREIGN);
+ }
+ if (!he || !he->nwords) {
unmetafy_line();
return 1;
}
diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c
index 49b2a26ad..d90838f03 100644
--- a/Src/Zle/zle_keymap.c
+++ b/Src/Zle/zle_keymap.c
@@ -424,7 +424,7 @@ scankeys(HashNode hn, UNUSED(int flags))
/**************************/
/**/
-Keymap
+mod_export Keymap
openkeymap(char *name)
{
KeymapName n = (KeymapName) keymapnamtab->getnode(keymapnamtab, name);
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index be68f4722..9edf30e01 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -906,6 +906,8 @@ getbyte(long do_keytmout, int *timeout, int full)
continue;
stopmsg = 1;
zexit(1, ZEXIT_NORMAL);
+ /* If called from an exit hook, zexit() returns, so: */
+ break;
}
icnt = 0;
if (errno == EINTR) {
@@ -929,6 +931,8 @@ getbyte(long do_keytmout, int *timeout, int full)
zerr("error on TTY read: %e", errno);
stopmsg = 1;
zexit(1, ZEXIT_NORMAL);
+ /* If called from an exit hook, zexit() returns, so: */
+ break;
}
}
if (cc == '\r') /* undo the exchange of \n and \r determined by */
@@ -1056,7 +1060,7 @@ getrestchar(int inchar, char *outstr, int *outcount)
#endif
/**/
-void
+mod_export void
redrawhook(void)
{
Thingy initthingy;
@@ -1065,6 +1069,7 @@ redrawhook(void)
int saverrflag = errflag, savretflag = retflag;
int lastcmd_prev = lastcmd;
int old_incompfunc = incompfunc;
+ int old_viinrepeat = viinrepeat;
char *args[2];
Thingy lbindk_save = lbindk, bindk_save = bindk;
@@ -1079,6 +1084,7 @@ redrawhook(void)
incompfunc = 0;
execzlefunc(initthingy, args, 1, 0);
incompfunc = old_incompfunc;
+ viinrepeat = old_viinrepeat;
/* Restore errflag and retflag as zlecallhook() does */
errflag = saverrflag | (errflag & ERRFLAG_INT);
diff --git a/Src/Zle/zle_misc.c b/Src/Zle/zle_misc.c
index 612ac2138..eba28d1ec 100644
--- a/Src/Zle/zle_misc.c
+++ b/Src/Zle/zle_misc.c
@@ -1508,9 +1508,6 @@ static struct suffixset *suffixlist;
/**/
static char *suffixfunc;
-/* Length associated with the suffix function */
-static int suffixfunclen;
-
/* Whether to remove suffix on uninsertable characters */
/**/
int suffixnoinsrem;
@@ -1611,7 +1608,7 @@ makesuffixstr(char *f, char *s, int n)
if (f) {
zsfree(suffixfunc);
suffixfunc = ztrdup(f);
- suffixfunclen = n;
+ suffixlen = n;
} else if (s) {
int inv, i, z = 0;
ZLE_STRING_T ws, lasts, wptr;
@@ -1685,7 +1682,7 @@ iremovesuffix(ZLE_INT_T c, int keep)
unmetafy_line();
}
- sprintf(buf, "%d", suffixfunclen);
+ sprintf(buf, "%d", suffixlen);
addlinknode(args, suffixfunc);
addlinknode(args, buf);
@@ -1800,5 +1797,5 @@ fixsuffix(void)
suffixlist = next;
}
- suffixfunclen = suffixnoinsrem = suffixlen = 0;
+ suffixnoinsrem = suffixlen = 0;
}
diff --git a/Src/Zle/zle_move.c b/Src/Zle/zle_move.c
index 155fda80d..3bafff3f1 100644
--- a/Src/Zle/zle_move.c
+++ b/Src/Zle/zle_move.c
@@ -166,7 +166,7 @@ decpos(int *pos)
*/
/**/
-char *
+mod_export char *
backwardmetafiedchar(char *start, char *endptr, convchar_t *retchr)
{
#ifdef MULTIBYTE_SUPPORT
diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c
index 7b8593dec..30b5d4447 100644
--- a/Src/Zle/zle_refresh.c
+++ b/Src/Zle/zle_refresh.c
@@ -212,9 +212,9 @@ static zattr default_atr_on, special_atr_on;
/*
* Array of region highlights, no special termination.
- * The first element (0) always describes the region between
- * point and mark. Any other elements are set by the user
- * via the parameter region_highlight.
+ * The first N_SPECIAL_HIGHLIGHTS elements describe special uses of
+ * highlighting, documented under N_SPECIAL_HIGHLIGHTS.
+ * Any other elements are set by the user via the parameter region_highlight.
*/
/**/
@@ -255,7 +255,9 @@ int cost;
#endif
static const REFRESH_ELEMENT zr_cr = { ZWC('\r'), 0 };
+#ifdef MULTIBYTE_SUPPORT
static const REFRESH_ELEMENT zr_dt = { ZWC('.'), 0 };
+#endif
static const REFRESH_ELEMENT zr_nl = { ZWC('\n'), 0 };
static const REFRESH_ELEMENT zr_sp = { ZWC(' '), 0 };
static const REFRESH_ELEMENT zr_zr = { ZWC('\0'), 0 };
@@ -414,16 +416,19 @@ get_region_highlight(UNUSED(Param pm))
arrsize--;
rhp++, arrp++) {
char digbuf1[DIGBUFSIZE], digbuf2[DIGBUFSIZE];
- int atrlen = 0, alloclen;
+ 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 0 */
+ 3; /* 2 spaces, 1 terminating NUL */
if (rhp->flags & ZRH_PREDISPLAY)
alloclen += 2; /* "P " */
+ if (rhp->memo)
+ alloclen += 1 /* space */ + strlen(memo_equals) + strlen(rhp->memo);
*arrp = (char *)zhalloc(alloclen * sizeof(char));
/*
* On input we allow a space after the flags.
@@ -436,6 +441,12 @@ get_region_highlight(UNUSED(Param pm))
(rhp->flags & ZRH_PREDISPLAY) ? "P" : "",
digbuf1, digbuf2);
(void)output_highlight(rhp->atr, *arrp + strlen(*arrp));
+
+ if (rhp->memo) {
+ strcat(*arrp, " ");
+ strcat(*arrp, memo_equals);
+ strcat(*arrp, rhp->memo);
+ }
}
*arrp = NULL;
return retarr;
@@ -460,6 +471,8 @@ set_region_highlight(UNUSED(Param pm), char **aval)
/* no null termination, but include special highlighting at start */
int newsize = len + N_SPECIAL_HIGHLIGHTS;
int diffsize = newsize - n_region_highlights;
+
+ free_region_highlights_memos();
region_highlights = (struct region_highlight *)
zrealloc(region_highlights,
sizeof(struct region_highlight) * newsize);
@@ -476,6 +489,7 @@ set_region_highlight(UNUSED(Param pm), char **aval)
*aval;
rhp++, aval++) {
char *strp, *oldstrp;
+ const char memo_equals[] = "memo=";
oldstrp = *aval;
if (*oldstrp == 'P') {
@@ -502,7 +516,44 @@ set_region_highlight(UNUSED(Param pm), char **aval)
while (inblank(*strp))
strp++;
- match_highlight(strp, &rhp->atr);
+ strp = (char*) match_highlight(strp, &rhp->atr);
+
+ while (inblank(*strp))
+ strp++;
+
+ if (strpfx(memo_equals, strp)) {
+ const char *memo_start = strp + strlen(memo_equals);
+ const char *i, *memo_end;
+
+ /*
+ * Forward compatibility: end parsing at a comma or whitespace to
+ * allow the following extensions:
+ *
+ * - A fifth field: "0 20 bold memo=foo bar".
+ *
+ * - Additional attributes in the fourth field: "0 20 bold memo=foo,bar"
+ * and "0 20 bold memo=foo\0bar".
+ *
+ * For similar reasons, we don't flag an error if the fourth field
+ * doesn't start with "memo=" as we expect.
+ */
+ i = memo_start;
+
+ /* ### TODO: Consider optimizing the common case that memo_start to
+ * end-of-string is entirely ASCII */
+ while (1) {
+ int nbytes;
+ convchar_t c = unmeta_one(i, &nbytes);
+
+ if (c == '\0' || c == ',' || inblank(c)) {
+ memo_end = i;
+ break;
+ } else
+ i += nbytes;
+ }
+ rhp->memo = ztrduppfx(memo_start, memo_end - memo_start);
+ } else
+ rhp->memo = NULL;
}
freearray(av);
@@ -2797,6 +2848,7 @@ zle_refresh_finish(void)
if (region_highlights)
{
+ free_region_highlights_memos();
zfree(region_highlights,
sizeof(struct region_highlight) * n_region_highlights);
region_highlights = NULL;
diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c
index ce61db27b..cd3f2c356 100644
--- a/Src/Zle/zle_thingy.c
+++ b/Src/Zle/zle_thingy.c
@@ -678,6 +678,7 @@ bin_zle_flags(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
else if (!strcmp(*flag, "keepsuffix"))
w->flags |= ZLE_KEEPSUFFIX;
*/
+ /* If you add magic strings here, be consistent with bin_zle_call() */
else if (!strcmp(*flag, "vichange")) {
if (invicmdmode()) {
startvichange(-1);
@@ -703,7 +704,7 @@ bin_zle_call(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
{
Thingy t;
struct modifier modsave = zmod;
- int ret, saveflag = 0, setbindk = 0, setlbindk, remetafy;
+ int ret, saveflag = 0, setbindk = 0, setlbindk = 0, remetafy;
char *wname = *args++, *keymap_restore = NULL, *keymap_tmp;
if (!wname)
@@ -727,12 +728,26 @@ bin_zle_call(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
while (*args && **args == '-') {
char skip_this_arg[2] = "x";
char *num;
+ char *flag;
if (!args[0][1] || args[0][1] == '-') {
args++;
break;
}
while (*++(*args)) {
switch (**args) {
+ case 'f':
+ flag = args[0][1] ? args[0]+1 : args[1];
+ if (flag == NULL || strcmp(flag, "nolast")) {
+ zwarnnam(name, "%s", "'nolast' expected after -f");
+ if (remetafy)
+ metafy_line();
+ return 1;
+ }
+ if (!args[0][1])
+ *++args = skip_this_arg;
+ /* If you add magic strings here, be consistent with bin_zle_flags() */
+ setlbindk = 1;
+ break;
case 'n':
num = args[0][1] ? args[0]+1 : args[1];
if (!num) {
@@ -787,7 +802,7 @@ bin_zle_call(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
* a vi range to detect a repeated key */
setbindk = setbindk ||
(t->widget && (t->widget->flags & (WIDGET_INT | ZLE_VIOPER)) == WIDGET_INT);
- setlbindk = t->widget && (t->widget->flags & ZLE_NOLAST) == ZLE_NOLAST;
+ setlbindk |= t->widget && (t->widget->flags & ZLE_NOLAST) == ZLE_NOLAST;
ret = execzlefunc(t, args, setbindk, setlbindk);
unrefthingy(t);
if (saveflag)
diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c
index 2b306fdcd..526216fa7 100644
--- a/Src/Zle/zle_utils.c
+++ b/Src/Zle/zle_utils.c
@@ -557,6 +557,22 @@ zlegetline(int *ll, int *cs)
}
+/*
+ * free() the 'memo' elements of region_highlights.
+ */
+
+/**/
+void
+free_region_highlights_memos(void)
+{
+ struct region_highlight *rhp;
+ for (rhp = region_highlights;
+ rhp < region_highlights + n_region_highlights;
+ rhp++) {
+ zfree((char*) rhp->memo, 0);
+ }
+}
+
/* Forward reference */
struct zle_region;
@@ -568,6 +584,7 @@ struct zle_region {
int start;
int end;
int flags;
+ const char *memo;
};
/* Forward reference */
@@ -632,6 +649,7 @@ zle_save_positions(void)
newrhp->next = NULL;
newrhp->atr = rhp->atr;
newrhp->flags = rhp->flags;
+ newrhp->memo = ztrdup(rhp->memo);
if (zlemetaline) {
newrhp->start = rhp->start_meta;
newrhp->end = rhp->end_meta;
@@ -682,6 +700,7 @@ zle_restore_positions(void)
nreg++, oldrhp = oldrhp->next)
;
if (nreg + N_SPECIAL_HIGHLIGHTS != n_region_highlights) {
+ free_region_highlights_memos();
n_region_highlights = nreg + N_SPECIAL_HIGHLIGHTS;
region_highlights = (struct region_highlight *)
zrealloc(region_highlights,
@@ -694,6 +713,7 @@ zle_restore_positions(void)
rhp->atr = oldrhp->atr;
rhp->flags = oldrhp->flags;
+ rhp->memo = oldrhp->memo; /* transferring ownership of the permanently-allocated memory */
if (zlemetaline) {
rhp->start_meta = oldrhp->start;
rhp->end_meta = oldrhp->end;
@@ -707,6 +727,7 @@ zle_restore_positions(void)
rhp++;
}
} else if (region_highlights) {
+ free_region_highlights_memos();
zfree(region_highlights, sizeof(struct region_highlight) *
n_region_highlights);
region_highlights = NULL;
@@ -1509,7 +1530,7 @@ mkundoent(void)
struct change *ch;
UNMETACHECK();
- if(lastll == zlell && !ZS_memcmp(lastline, zleline, zlell)) {
+ if(lastll == zlell && lastlinesz >= zlell && !ZS_memcmp(lastline, zleline, zlell)) {
lastcs = zlecs;
return;
}
diff --git a/Src/builtin.c b/Src/builtin.c
index aa5767cf1..8ef678b22 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -71,7 +71,7 @@ static struct builtin builtins[] =
* But that's actually not useful, so it's more consistent to
* cause an error.
*/
- BUILTIN("fc", 0, bin_fc, 0, -1, BIN_FC, "aAdDe:EfiIlLmnpPrRt:W", NULL),
+ BUILTIN("fc", 0, bin_fc, 0, -1, BIN_FC, "aAdDe:EfiIlLmnpPrRst:W", NULL),
BUILTIN("fg", 0, bin_fg, 0, -1, BIN_FG, NULL, NULL),
BUILTIN("float", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "E:%F:%HL:%R:%Z:%ghlp:%rtux", "E"),
BUILTIN("functions", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "ckmMstTuUWx:z", NULL),
@@ -89,7 +89,6 @@ static struct builtin builtins[] =
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("log", 0, bin_log, 0, 0, 0, NULL, NULL),
BUILTIN("logout", 0, bin_break, 0, 1, BIN_LOGOUT, NULL, NULL),
#if defined(ZSH_MEM) & defined(ZSH_MEM_DEBUG)
@@ -841,7 +840,6 @@ int
bin_cd(char *nam, char **argv, Options ops, int func)
{
LinkNode dir;
- struct stat st1, st2;
if (isset(RESTRICTED)) {
zwarnnam(nam, "restricted");
@@ -860,23 +858,6 @@ bin_cd(char *nam, char **argv, Options ops, int func)
}
cd_new_pwd(func, dir, OPT_ISSET(ops, 'q'));
- if (stat(unmeta(pwd), &st1) < 0) {
- setjobpwd();
- zsfree(pwd);
- pwd = NULL;
- pwd = metafy(zgetcwd(), -1, META_DUP);
- } else if (stat(".", &st2) < 0) {
- if (chdir(unmeta(pwd)) < 0)
- zwarn("unable to chdir(%s): %e", pwd, errno);
- } else if (st1.st_ino != st2.st_ino || st1.st_dev != st2.st_dev) {
- if (chasinglinks) {
- setjobpwd();
- zsfree(pwd);
- pwd = NULL;
- pwd = metafy(zgetcwd(), -1, META_DUP);
- } else if (chdir(unmeta(pwd)) < 0)
- zwarn("unable to chdir(%s): %e", pwd, errno);
- }
unqueue_signals();
return 0;
}
@@ -1210,6 +1191,7 @@ static void
cd_new_pwd(int func, LinkNode dir, int quiet)
{
char *new_pwd, *s;
+ struct stat st1, st2;
int dirstacksize;
if (func == BIN_PUSHD)
@@ -1239,6 +1221,22 @@ cd_new_pwd(int func, LinkNode dir, int quiet)
}
}
+ if (stat(unmeta(new_pwd), &st1) < 0) {
+ zsfree(new_pwd);
+ new_pwd = NULL;
+ new_pwd = metafy(zgetcwd(), -1, META_DUP);
+ } else if (stat(".", &st2) < 0) {
+ if (chdir(unmeta(new_pwd)) < 0)
+ zwarn("unable to chdir(%s): %e", new_pwd, errno);
+ } else if (st1.st_ino != st2.st_ino || st1.st_dev != st2.st_dev) {
+ if (chasinglinks) {
+ zsfree(new_pwd);
+ new_pwd = NULL;
+ new_pwd = metafy(zgetcwd(), -1, META_DUP);
+ } else if (chdir(unmeta(new_pwd)) < 0)
+ zwarn("unable to chdir(%s): %e", new_pwd, errno);
+ }
+
/* shift around the pwd variables, to make oldpwd and pwd relate to the
current (i.e. new) pwd */
zsfree(oldpwd);
@@ -1600,8 +1598,9 @@ bin_fc(char *nam, char **argv, Options ops, int func)
* command line to avoid giving the user a nasty turn
* if some helpful soul ran "print -s 'rm -rf /'".
*/
- first = OPT_ISSET(ops,'l')? addhistnum(curhist,-16,0)
- : addhistnum(curline.histnum,-1,0);
+ int xflags = OPT_ISSET(ops,'L') ? HIST_FOREIGN : 0;
+ first = OPT_ISSET(ops,'l')? addhistnum(curhist,-16,xflags)
+ : addhistnum(curline.histnum,-1,xflags);
if (first < 1)
first = 1;
if (last < first)
@@ -1644,7 +1643,7 @@ bin_fc(char *nam, char **argv, Options ops, int func)
if (!fclist(out, ops, first, last, asgf, pprog, 1)) {
char *editor;
- if (func == BIN_R)
+ if (func == BIN_R || OPT_ISSET(ops, 's'))
editor = "-";
else if (OPT_HASARG(ops, 'e'))
editor = OPT_ARG(ops, 'e');
@@ -2024,7 +2023,7 @@ typeset_setwidth(const char * name, Param pm, Options ops, int on, int always)
/**/
static Param
-typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
+typeset_single(char *cname, char *pname, Param pm, int func,
int on, int off, int roff, Asgment asg, Param altpm,
Options ops, int joinchar)
{
@@ -2280,7 +2279,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
} else if (asg->flags & ASG_ARRAY) {
int flags = (asg->flags & ASG_KEY_VALUE) ? ASSPM_KEY_VALUE : 0;
if (!(pm = assignaparam(pname, asg->value.array ?
- zlinklist2array(asg->value.array) :
+ zlinklist2array(asg->value.array, 1) :
mkarray(NULL), flags)))
return NULL;
}
@@ -2442,7 +2441,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
} else if (PM_TYPE(on) == PM_ARRAY && ASG_ARRAYP(asg)) {
int flags = (asg->flags & ASG_KEY_VALUE) ? ASSPM_KEY_VALUE : 0;
if (!(pm = assignaparam(pname, asg->value.array ?
- zlinklist2array(asg->value.array) :
+ zlinklist2array(asg->value.array, 1) :
mkarray(NULL), flags)))
return NULL;
dont_set = 1;
@@ -2480,13 +2479,19 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
return NULL;
}
if (on & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z)) {
- if (typeset_setwidth(cname, pm, ops, on, 0))
+ if (typeset_setwidth(cname, pm, ops, on, 0)) {
+ unsetparam_pm(pm, 0, 1);
return NULL;
+ }
}
if (on & (PM_INTEGER | PM_EFLOAT | PM_FFLOAT)) {
- if (typeset_setbase(cname, pm, ops, on, 0))
+ if (typeset_setbase(cname, pm, ops, on, 0)) {
+ unsetparam_pm(pm, 0, 1);
return NULL;
+ }
}
+ if (isset(TYPESETTOUNSET))
+ pm->node.flags |= PM_DEFAULTED;
} else {
if (idigit(*pname))
zerrnam(cname, "not an identifier: %s", pname);
@@ -2503,8 +2508,10 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
*/
struct tieddata *tdp = (struct tieddata *)
zalloc(sizeof(struct tieddata));
- if (!tdp)
+ if (!tdp) {
+ unsetparam_pm(pm, 0, 1);
return NULL;
+ }
tdp->joinchar = joinchar;
tdp->arrptr = &altpm->u.arr;
@@ -2536,7 +2543,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
arrayval = mkarray(NULL);
}
} else if (asg->value.array)
- arrayval = zlinklist2array(asg->value.array);
+ arrayval = zlinklist2array(asg->value.array, 1);
else
arrayval = mkarray(NULL);
if (!(pm=assignaparam(pname, arrayval, flags)))
@@ -2597,7 +2604,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
*/
/**/
-int
+mod_export int
bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
{
Param pm;
@@ -2607,7 +2614,12 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
int on = 0, off = 0, roff, bit = PM_ARRAY;
int i;
int returnval = 0, printflags = 0;
- int hasargs;
+ int hasargs = *argv != NULL || (assigns && firstnode(assigns));
+
+ /* POSIXBUILTINS is set for bash/ksh and both ignore -p with args */
+ if ((func == BIN_READONLY || func == BIN_EXPORT) &&
+ isset(POSIXBUILTINS) && hasargs)
+ ops->ind['p'] = 0;
/* hash -f is really the builtin `functions' */
if (OPT_ISSET(ops,'f'))
@@ -2687,7 +2699,6 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
/* -p0 treated as -p for consistency */
}
}
- hasargs = *argv != NULL || (assigns && firstnode(assigns));
if (!hasargs) {
int exclude = 0;
if (!OPT_ISSET(ops,'p')) {
@@ -2830,7 +2841,7 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
unqueue_signals();
return 1;
} else if (pm) {
- if (!(pm->node.flags & PM_UNSET)
+ if ((!(pm->node.flags & PM_UNSET) || pm->node.flags & PM_DECLARED)
&& (locallevel == pm->level || !(on & PM_LOCAL))) {
if (pm->node.flags & PM_TIED) {
if (PM_TYPE(pm->node.flags) != PM_SCALAR) {
@@ -2883,6 +2894,8 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
*
* Don't attempt to set it yet, it's too early
* to be exported properly.
+ *
+ * This may create the array with PM_DEFAULTED.
*/
asg2.name = asg->name;
asg2.flags = 0;
@@ -2923,9 +2936,13 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
apm->ename = ztrdup(asg0.name);
if (asg->value.array) {
int flags = (asg->flags & ASG_KEY_VALUE) ? ASSPM_KEY_VALUE : 0;
- assignaparam(asg->name, zlinklist2array(asg->value.array), flags);
- } else if (oldval)
- assignsparam(asg0.name, oldval, 0);
+ assignaparam(asg->name, zlinklist2array(asg->value.array, 1), flags);
+ } else if (asg0.value.scalar || oldval) {
+ /* We have to undo what we did wrong with asg2 */
+ apm->node.flags &= ~PM_DEFAULTED;
+ if (oldval)
+ assignsparam(asg0.name, oldval, 0);
+ }
unqueue_signals();
return 0;
@@ -3706,14 +3723,12 @@ bin_unset(char *name, char **argv, Options ops, int func)
while ((s = *argv++)) {
char *ss = strchr(s, '['), *subscript = 0;
if (ss) {
- char *sse;
+ char *sse = ss + strlen(ss)-1;
*ss = 0;
- if ((sse = parse_subscript(ss+1, 1, ']'))) {
+ if (*sse == ']') {
*sse = 0;
subscript = dupstring(ss+1);
*sse = ']';
- remnulargs(subscript);
- untokenize(subscript);
}
}
if ((ss && !subscript) || !isident(s)) {
@@ -3901,7 +3916,7 @@ bin_whence(char *nam, char **argv, Options ops, int func)
}
unqueue_signals();
if (all) {
- allmatched = argv = zlinklist2array(matchednodes);
+ allmatched = argv = zlinklist2array(matchednodes, 1);
matchednodes = NULL;
popheap();
} else
@@ -4817,7 +4832,7 @@ bin_print(char *name, char **args, Options ops, int func)
{
fwrite(args[n], len[n], 1, fout);
l = widths[n];
- if (n < argc)
+ if (n < argc && ic < nc - 1)
for (; l < sc; l++)
fputc(' ', fout);
}
@@ -4857,7 +4872,7 @@ bin_print(char *name, char **args, Options ops, int func)
/* normal output */
if (!fmt) {
- if (OPT_ISSET(ops, 'z') || OPT_ISSET(ops, 'v') ||
+ if (OPT_ISSET(ops, 'z') ||
OPT_ISSET(ops, 's') || OPT_ISSET(ops, 'S')) {
/*
* We don't want the arguments unmetafied after all.
@@ -5550,6 +5565,11 @@ bin_getopts(UNUSED(char *name), char **argv, UNUSED(Options ops), UNUSED(int fun
/* check for legality */
if(opch == ':' || !(p = memchr(optstr, opch, lenoptstr))) {
p = "?";
+ /* Keep OPTIND correct if the user doesn't return after the error */
+ if (isset(POSIXBUILTINS)) {
+ optcind = 0;
+ zoptind++;
+ }
zsfree(zoptarg);
setsparam(var, ztrdup(p));
if(quiet) {
@@ -5566,6 +5586,11 @@ bin_getopts(UNUSED(char *name), char **argv, UNUSED(Options ops), UNUSED(int fun
if(p[1] == ':') {
if(optcind == lenstr) {
if(!args[zoptind]) {
+ /* Fix OPTIND as above */
+ if (isset(POSIXBUILTINS)) {
+ optcind = 0;
+ zoptind++;
+ }
zsfree(zoptarg);
if(quiet) {
setsparam(var, ztrdup(":"));
@@ -5607,13 +5632,16 @@ bin_getopts(UNUSED(char *name), char **argv, UNUSED(Options ops), UNUSED(int fun
*/
/**/
-mod_export int
-exit_pending;
+mod_export volatile int exit_pending;
/* Shell level at which we exit if exit_pending */
/**/
-mod_export int
-exit_level;
+mod_export volatile int exit_level;
+
+/* we have printed a 'you have stopped (running) jobs.' message */
+
+/**/
+mod_export volatile int stopmsg;
/* break, bye, continue, exit, logout, return -- most of these take *
* one numeric argument, and the other (logout) is related to return. *
@@ -5705,11 +5733,6 @@ bin_break(char *name, char **argv, UNUSED(Options ops), int func)
return 0;
}
-/* we have printed a 'you have stopped (running) jobs.' message */
-
-/**/
-mod_export int stopmsg;
-
/* check to see if user has jobs running/stopped */
/**/
@@ -5800,8 +5823,11 @@ zexit(int val, enum zexit_t from_where)
* a later value always overrides an earlier.
*/
exit_val = val;
- if (shell_exiting == -1)
+ if (shell_exiting == -1) {
+ retflag = 1;
+ breaks = loops;
return;
+ }
if (isset(MONITOR) && !stopmsg && from_where != ZEXIT_SIGNAL) {
scanjobs(); /* check if jobs need printing */
@@ -6119,7 +6145,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)) {
+ if (parseopts(nam, &argv, new_opts, &cmd, optlist, 0, NULL)) {
ret = 1;
goto restore;
}
diff --git a/Src/compat.c b/Src/compat.c
index 8ab335aa1..817bb4aaf 100644
--- a/Src/compat.c
+++ b/Src/compat.c
@@ -126,6 +126,32 @@ zgettime(struct timespec *ts)
return ret;
}
+/* Likewise with CLOCK_MONOTONIC if available. */
+
+/**/
+mod_export int
+zgettime_monotonic_if_available(struct timespec *ts)
+{
+ int ret = -1;
+
+#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
+ struct timespec dts;
+ if (clock_gettime(CLOCK_MONOTONIC, &dts) < 0) {
+ zwarn("unable to retrieve CLOCK_MONOTONIC time: %e", errno);
+ ret--;
+ } else {
+ ret++;
+ ts->tv_sec = (time_t) dts.tv_sec;
+ ts->tv_nsec = (long) dts.tv_nsec;
+ }
+#endif
+
+ if (ret) {
+ ret = zgettime(ts);
+ }
+ return ret;
+}
+
/* compute the difference between two calendar times */
@@ -496,7 +522,7 @@ zgetdir(struct dirsav *d)
*/
/**/
-char *
+mod_export char *
zgetcwd(void)
{
char *ret = zgetdir(NULL);
diff --git a/Src/exec.c b/Src/exec.c
index 50027654a..27d49e005 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -84,7 +84,7 @@ int nohistsave;
/* error flag: bits from enum errflag_bits */
/**/
-mod_export int errflag;
+mod_export volatile int errflag;
/*
* State of trap return value. Value is from enum trap_state.
@@ -122,7 +122,7 @@ int subsh;
/* != 0 if we have a return pending */
/**/
-mod_export int retflag;
+mod_export volatile int retflag;
/**/
long lastval2;
@@ -547,10 +547,29 @@ zexecve(char *pth, char **argv, char **newenvp)
}
}
} else if (eno == ENOEXEC) {
- for (t0 = 0; t0 != ct; t0++)
- if (!execvebuf[t0])
- break;
- if (t0 == ct) {
+ /* Perform binary safety check on classic shell *
+ * scripts (shebang wasn't introduced until UNIX *
+ * Seventh Edition). POSIX says we shall allow *
+ * execution of scripts with concatenated binary *
+ * and suggests checking a line exists before the *
+ * first NUL character with a lowercase letter or *
+ * expansion. This is consistent with FreeBSD sh. */
+ int isbinary, hasletter;
+ if (!(ptr2 = memchr(execvebuf, '\0', ct))) {
+ isbinary = 0;
+ } else {
+ isbinary = 1;
+ hasletter = 0;
+ for (ptr = execvebuf; ptr < ptr2; ptr++) {
+ if (islower(*ptr) || *ptr == '$' || *ptr == '`')
+ hasletter = 1;
+ if (hasletter && *ptr == '\n') {
+ isbinary = 0;
+ break;
+ }
+ }
+ }
+ if (!isbinary) {
argv[-1] = "sh";
winch_unblock();
execve("/bin/sh", argv - 1, newenvp);
@@ -665,8 +684,10 @@ execute(LinkList args, int flags, int defpath)
/* If the parameter STTY is set in the command's environment, *
* we first run the stty command with the value of this *
- * parameter as it arguments. */
- if ((s = STTYval) && isatty(0) && (GETPGRP() == getpid())) {
+ * parameter as it arguments. If the parameter is empty, we *
+ * do nothing, but this causes the terminal settings to be *
+ * restored later which can be useful. */
+ if ((s = STTYval) && *s && isatty(0) && (GETPGRP() == getpid())) {
char *t = tricat("stty", " ", s);
STTYval = 0; /* this prevents infinite recursion */
@@ -1014,7 +1035,8 @@ entersubsh(int flags, struct entersubsh_ret *retp)
if (!(flags & ESUB_KEEPTRAP))
for (sig = 0; sig < SIGCOUNT; sig++)
- if (!(sigtrapped[sig] & ZSIG_FUNC))
+ if (!(sigtrapped[sig] & ZSIG_FUNC) &&
+ !(isset(POSIXTRAPS) && (sigtrapped[sig] & ZSIG_IGNORED)))
unsettrap(sig);
monitor = isset(MONITOR);
job_control_ok = monitor && (flags & ESUB_JOB_CONTROL) && isset(POSIXJOBS);
@@ -1036,7 +1058,8 @@ entersubsh(int flags, struct entersubsh_ret *retp)
} else if (thisjob != -1 && (flags & ESUB_PGRP)) {
if (jobtab[list_pipe_job].gleader && (list_pipe || list_pipe_child)) {
if (setpgrp(0L, jobtab[list_pipe_job].gleader) == -1 ||
- killpg(jobtab[list_pipe_job].gleader, 0) == -1) {
+ (killpg(jobtab[list_pipe_job].gleader, 0) == -1 &&
+ errno == ESRCH)) {
jobtab[list_pipe_job].gleader =
jobtab[thisjob].gleader = (list_pipe_child ? mypgrp : getpid());
setpgrp(0L, jobtab[list_pipe_job].gleader);
@@ -1248,7 +1271,9 @@ execsimple(Estate state)
} else {
int q = queue_signal_level();
dont_queue_signals();
- if (code == WC_FUNCDEF)
+ if (errflag)
+ lv = errflag;
+ else if (code == WC_FUNCDEF)
lv = execfuncdef(state, NULL);
else
lv = (execfuncs[code - WC_CURSH])(state, 0);
@@ -1664,6 +1689,7 @@ execpline(Estate state, wordcode slcode, int how, int last1)
execpline2(state, code, how, opipe[0], ipipe[1], last1);
pline_level--;
if (how & Z_ASYNC) {
+ clearoldjobtab();
lastwj = newjob;
if (thisjob == list_pipe_job)
@@ -2142,14 +2168,15 @@ clobber_open(struct redir *f)
{
struct stat buf;
int fd, oerrno;
+ char *ufname = unmeta(f->name);
/* If clobbering, just open. */
if (isset(CLOBBER) || IS_CLOBBER_REDIR(f->type))
- return open(unmeta(f->name),
+ return open(ufname,
O_WRONLY | O_CREAT | O_TRUNC | O_NOCTTY, 0666);
/* If not clobbering, attempt to create file exclusively. */
- if ((fd = open(unmeta(f->name),
+ if ((fd = open(ufname,
O_WRONLY | O_CREAT | O_EXCL | O_NOCTTY, 0666)) >= 0)
return fd;
@@ -2157,11 +2184,27 @@ clobber_open(struct redir *f)
* Try opening, and if it's a regular file then close it again *
* because we weren't supposed to open it. */
oerrno = errno;
- if ((fd = open(unmeta(f->name), O_WRONLY | O_NOCTTY)) != -1) {
- if(!fstat(fd, &buf) && !S_ISREG(buf.st_mode))
- return fd;
+ if ((fd = open(ufname, O_WRONLY | O_NOCTTY)) != -1) {
+ if(!fstat(fd, &buf)) {
+ if (!S_ISREG(buf.st_mode))
+ return fd;
+ /*
+ * If CLOBBER_EMPTY is in effect and the file is empty,
+ * we are allowed to re-use it.
+ *
+ * Note: there is an intrinsic race here because another
+ * process can write to this file at any time. The only fix
+ * would be file locking, which we wish to avoid in basic
+ * file operations at this level. This would not be
+ * fixed. just additionally complicated, by re-opening the
+ * file and truncating.
+ */
+ if (isset(CLOBBEREMPTY) && buf.st_size == 0)
+ return fd;
+ }
close(fd);
}
+
errno = oerrno;
return -1;
}
@@ -2771,8 +2814,7 @@ execcmd_fork(Estate state, int how, int type, Wordcode varspc,
/* Check if we should run background jobs at a lower priority. */
if ((how & Z_ASYNC) && isset(BGNICE)) {
errno = 0;
- nice(5);
- if (errno)
+ if (nice(5) == -1 && errno)
zwarn("nice(5) failed: %e", errno);
}
#endif /* HAVE_NICE */
@@ -2865,11 +2907,13 @@ execcmd_exec(Estate state, Execcmd_params eparams,
pushnode(args, dupstring("fg"));
}
- if ((how & Z_ASYNC) || output) {
+ if ((how & Z_ASYNC) || output ||
+ (last1 == 2 && input && EMULATION(EMULATE_SH))) {
/*
- * If running in the background, or not the last command in a
- * pipeline, we don't need any of the rest of this function to
- * affect the state in the main shell, so fork immediately.
+ * If running in the background, not the last command in a
+ * pipeline, or the last command in a multi-stage pipeline
+ * in sh mode, we don't need any of the rest of this function
+ * to affect the state in the main shell, so fork immediately.
*
* In other cases we may need to process the command line
* a bit further before we make the decision.
@@ -3383,7 +3427,7 @@ execcmd_exec(Estate state, Execcmd_params eparams,
int rmall;
s[l - 2] = 0;
- rmall = checkrmall(s);
+ rmall = checkrmall(l == 2 ? "/" : s);
s[l - 2] = t;
if (!rmall) {
@@ -3913,7 +3957,7 @@ execcmd_exec(Estate state, Execcmd_params eparams,
if (type == WC_AUTOFN) {
/*
* We pre-loaded this to get any redirs.
- * So we execuate a simplified function here.
+ * So we execute a simplified function here.
*/
lastval = execautofn_basic(state, do_exec);
} else
@@ -3968,8 +4012,8 @@ execcmd_exec(Estate state, Execcmd_params eparams,
if (is_shfunc) {
/* It's a shell function */
- pipecleanfilelist(filelist, 0);
execshfunc((Shfunc) hn, args);
+ pipecleanfilelist(filelist, 0);
} else {
/* It's a builtin */
LinkList assigns = (LinkList)0;
@@ -4581,7 +4625,7 @@ getoutput(char *cmd, int qt)
char *s;
int onc = nocomments;
- nocomments = (interact && unset(INTERACTIVECOMMENTS));
+ nocomments = (interact && !sourcelevel && unset(INTERACTIVECOMMENTS));
prog = parse_string(cmd, 0);
nocomments = onc;
@@ -4793,8 +4837,10 @@ getoutputfile(char *cmd, char **eptr)
singsub(&s);
if (errflag)
s = NULL;
- else
+ else {
untokenize(s);
+ s = dyncat(s, "\n");
+ }
}
if (!s) /* Unclear why we need to do this before open() */
@@ -5146,33 +5192,51 @@ exectime(Estate state, UNUSED(int do_exec))
return lastval;
}
-/* Define a shell function */
-
+/* The string displayed in lieu of the name of an anonymous function (in PS4,
+ * zprof output, etc)
+ */
static const char *const ANONYMOUS_FUNCTION_NAME = "(anon)";
+/*
+ * Take a function name argument and return true iff it is equal to the string
+ * used for the names of anonymous functions, "(anon)".
+ *
+ * Note that it's possible to define a named function literally called "(anon)"
+ * (though I doubt anyone would ever do that).
+ */
+/**/
+int is_anonymous_function_name(const char *name)
+{
+ return !strcmp(name, ANONYMOUS_FUNCTION_NAME);
+}
+
+/* Define a shell function */
+
/**/
static int
execfuncdef(Estate state, Eprog redir_prog)
{
Shfunc shf;
char *s = NULL;
- int signum, nprg, sbeg, nstrs, npats, len, plen, i, htok = 0, ret = 0;
+ int signum, nprg, sbeg, nstrs, npats, do_tracing, len, plen, i, htok = 0, ret = 0;
int anon_func = 0;
Wordcode beg = state->pc, end;
Eprog prog;
Patprog *pp;
LinkList names;
+ int tracing_flags;
end = beg + WC_FUNCDEF_SKIP(state->pc[-1]);
names = ecgetlist(state, *state->pc++, EC_DUPTOK, &htok);
- nprg = end - beg;
sbeg = *state->pc++;
nstrs = *state->pc++;
npats = *state->pc++;
+ do_tracing = *state->pc++;
nprg = (end - state->pc);
plen = nprg * sizeof(wordcode);
len = plen + (npats * sizeof(Patprog)) + nstrs;
+ tracing_flags = do_tracing ? PM_TAGGED_LOCAL : 0;
if (htok && names) {
execsubst(names);
@@ -5222,7 +5286,7 @@ execfuncdef(Estate state, Eprog redir_prog)
shf = (Shfunc) zalloc(sizeof(*shf));
shf->funcdef = prog;
- shf->node.flags = 0;
+ shf->node.flags = tracing_flags;
/* No dircache here, not a directory */
shf->filename = ztrdup(scriptfilename);
shf->lineno =
@@ -5317,6 +5381,12 @@ execfuncdef(Estate state, Eprog redir_prog)
*/
removetrapnode(signum);
}
+ /* Is this function traced and redefining itself? */
+ 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);
+ }
shfunctab->addnode(shfunctab, ztrdup(s), shf);
}
}
@@ -6132,7 +6202,7 @@ stripkshdef(Eprog prog, char *name)
int sbeg = pc[2], nstrs = pc[3], nprg, npats = pc[4], plen, len, i;
Patprog *pp;
- pc += 5;
+ pc += 6;
nprg = end - pc;
plen = nprg * sizeof(wordcode);
diff --git a/Src/glob.c b/Src/glob.c
index f67a376b9..400be12d5 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -279,11 +279,11 @@ addpath(char *s, int l)
* foo/ can be used to reference a non-directory foo. Returns nonzero if *
* the file does not exists. */
-/**/
static int
statfullpath(const char *s, struct stat *st, int l)
{
char buf[PATH_MAX+1];
+ int check_for_being_a_directory = 0;
DPUTS(strlen(s) + !*s + pathpos - pathbufcwd >= PATH_MAX,
"BUG: statfullpath(): pathname too long");
@@ -294,16 +294,44 @@ statfullpath(const char *s, struct stat *st, int l)
* Don't add the '.' if the path so far is empty, since
* then we get bogus empty strings inserted as files.
*/
- buf[pathpos - pathbufcwd] = '.';
- buf[pathpos - pathbufcwd + 1] = '\0';
- l = 0;
+ if (st) {
+ buf[pathpos - pathbufcwd] = '.';
+ buf[pathpos - pathbufcwd + 1] = '\0';
+ l = 0;
+ }
+ else {
+ check_for_being_a_directory = 1;
+ }
}
unmetafy(buf, NULL);
- if (!st) {
+ if (st) {
+ return l ? lstat(buf, st) : stat(buf, st);
+ }
+ else if (check_for_being_a_directory) {
+ struct stat tmp;
+ if (stat(buf, &tmp))
+ return -1;
+
+ return S_ISDIR(tmp.st_mode) ? 0 : -1;
+ }
+ else {
char lbuf[1];
- return access(buf, F_OK) && (!l || readlink(buf, lbuf, 1) < 0);
+
+ /* If it exists, signal success. */
+ if (access(buf, F_OK) == 0)
+ return 0;
+
+ /* Would a dangling symlink be good enough? */
+ if (l == 0)
+ return -1;
+
+ /* Is it a dangling symlink? */
+ if (readlink(buf, lbuf, 1) >= 0)
+ return 0;
+
+ /* Guess it doesn't exist, then. */
+ return -1;
}
- return l ? lstat(buf, st) : stat(buf, st);
}
/* This may be set by qualifier functions to an array of strings to insert
@@ -327,11 +355,13 @@ insert(char *s, int checked)
if (gf_listtypes || gf_markdirs) {
/* Add the type marker to the end of the filename */
mode_t mode;
- checked = statted = 1;
if (statfullpath(s, &buf, 1)) {
unqueue_signals();
return;
}
+ else {
+ checked = statted = 1;
+ }
mode = buf.st_mode;
if (gf_follow) {
if (!S_ISLNK(mode) || statfullpath(s, &buf2, 0))
@@ -387,11 +417,10 @@ insert(char *s, int checked)
qn = qn->next;
}
} else if (!checked) {
- if (statfullpath(s, &buf, 1)) {
+ if (statfullpath(s, NULL, 1)) {
unqueue_signals();
return;
}
- statted = 1;
news = dyncat(pathbuf, news);
} else
news = dyncat(pathbuf, news);
@@ -2191,7 +2220,7 @@ bracechardots(char *str, convchar_t *c1p, convchar_t *c2p)
#ifdef MULTIBYTE_SUPPORT
cstart == WEOF ||
#else
- !cstart ||
+ !*pconv ||
#endif
pnext[0] != '.' || pnext[1] != '.')
return 0;
@@ -2212,7 +2241,7 @@ bracechardots(char *str, convchar_t *c1p, convchar_t *c2p)
#ifdef MULTIBYTE_SUPPORT
cend == WEOF ||
#else
- !cend ||
+ !*pconv ||
#endif
*pnext != Outbrace)
return 0;
@@ -2276,22 +2305,19 @@ xpandbraces(LinkList list, LinkNode *np)
strp = str - str3;
lenalloc = strp + strlen(str2+1) + 1;
do {
-#ifdef MULTIBYTE_SUPPORT
char *ncptr;
int nclen;
+#ifdef MULTIBYTE_SUPPORT
mb_charinit();
ncptr = wcs_nicechar(cend, NULL, NULL);
+#else
+ ncptr = nicechar(cend);
+#endif
nclen = strlen(ncptr);
p = zhalloc(lenalloc + nclen);
memcpy(p, str3, strp);
memcpy(p + strp, ncptr, nclen);
strcpy(p + strp + nclen, str2 + 1);
-#else
- p = zhalloc(lenalloc + 1);
- memcpy(p, str3, strp);
- sprintf(p + strp, "%c", cend);
- strcat(p + strp, str2 + 1);
-#endif
insertlinknode(list, last, p);
if (rev) /* decreasing: add in reverse order. */
last = nextnode(last);
@@ -2539,7 +2565,8 @@ get_match_ret(Imatchdata imd, int b, int e)
addlinknode(imd->repllist, rd);
return imd->mstr;
}
- ll += strlen(replstr);
+ if (replstr)
+ ll += strlen(replstr);
}
if (fl & SUB_MATCH) /* matched portion */
ll += 1 + (e - b);
@@ -2565,6 +2592,9 @@ get_match_ret(Imatchdata imd, int b, int e)
if (bl)
buf[bl - 1] = '\0';
+ if (ll == 0)
+ return NULL;
+
rr = r = (char *) hcalloc(ll);
if (fl & SUB_MATCH) {
@@ -3253,6 +3283,7 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr,
return 1;
}
if (matched) {
+ /* Default is to match at the start; see comment in MULTIBYTE above */
switch (fl & (SUB_END|SUB_LONG|SUB_SUBSTR)) {
case 0:
case SUB_LONG:
@@ -3300,7 +3331,7 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr,
/* Largest possible match at tail of string: *
* move forward along string until we get a match. *
* Again there's no optimisation. */
- for (ioff = 0, t = s, umlen = uml; t < send;
+ for (ioff = 0, t = s, umlen = uml; t <= send;
ioff++, t++, umlen--) {
set_pat_start(p, t-s);
if (pattrylen(p, t, send - t, umlen, &patstralloc, ioff)) {
@@ -3322,7 +3353,7 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr,
/* longest or smallest at start with substrings */
t = s;
if (fl & SUB_GLOBAL) {
- imd.repllist = newlinklist();
+ imd.repllist = (fl & SUB_LIST) ? znewlinklist() : newlinklist();
if (repllistp)
*repllistp = imd.repllist;
}
@@ -3331,7 +3362,7 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr,
do {
/* loop over all matches for global substitution */
matched = 0;
- for (; t < send; t++, ioff++, umlen--) {
+ for (; t <= send; t++, ioff++, umlen--) {
/* Find the longest match from this position. */
set_pat_start(p, t-s);
if (pattrylen(p, t, send - t, umlen, &patstralloc, ioff)) {
@@ -3372,6 +3403,8 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr,
* which is already marked for replacement.
*/
matched = 1;
+ if (t == send)
+ break;
while (t < mpos) {
ioff++;
umlen--;
@@ -3379,8 +3412,10 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr,
}
break;
}
+ if (t == send)
+ break;
}
- } while (matched);
+ } while (matched && t < send);
/*
* check if we can match a blank string, if so do it
* at the start. Goodness knows if this is a good idea
@@ -3452,6 +3487,8 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr,
* Results from get_match_ret in repllist are all metafied.
*/
s = *sp;
+ if (fl & SUB_LIST)
+ return 1;
i = 0; /* start of last chunk we got from *sp */
for (nd = firstnode(imd.repllist); nd; incnode(nd)) {
rd = (Repldata) getdata(nd);
@@ -3481,7 +3518,7 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr,
imd.replstr = NULL;
imd.repllist = NULL;
*sp = get_match_ret(&imd, 0, 0);
- return 1;
+ return (fl & SUB_RETFAIL) ? 0 : 1;
}
/**/
diff --git a/Src/hashnameddir.c b/Src/hashnameddir.c
index bed43d025..bab7b64b2 100644
--- a/Src/hashnameddir.c
+++ b/Src/hashnameddir.c
@@ -42,17 +42,6 @@
/* Named Directory Hash Table Functions */
/****************************************/
-#ifdef HAVE_NIS_PLUS
-# include <rpcsvc/nis.h>
-#else
-# ifdef HAVE_NIS
-# include <rpc/types.h>
-# include <rpc/rpc.h>
-# include <rpcsvc/ypclnt.h>
-# include <rpcsvc/yp_prot.h>
-# endif
-#endif
-
/* hash table containing named directories */
/**/
@@ -102,122 +91,11 @@ emptynameddirtable(HashTable ht)
/* Add all the usernames in the password file/database *
* to the named directories table. */
-#ifdef HAVE_NIS_PLUS
-static int
-add_userdir(nis_name table, nis_object *object, void *userdata)
-{
- if (object->zo_data.objdata_u.en_data.en_cols.en_cols_len >= 6) {
- static char name[40], dir[PATH_MAX + 1];
- register entry_col *ec =
- object->zo_data.objdata_u.en_data.en_cols.en_cols_val;
- register int nl = minimum(ec[0].ec_value.ec_value_len, 39);
- register int dl = minimum(ec[5].ec_value.ec_value_len, PATH_MAX);
-
- memcpy(name, ec[0].ec_value.ec_value_val, nl);
- name[nl] = '\0';
- memcpy(dir, ec[5].ec_value.ec_value_val, dl);
- dir[dl] = '\0';
-
- adduserdir(name, dir, ND_USERNAME, 1);
- }
- return 0;
-}
-#else
-# ifdef HAVE_NIS
-static int
-add_userdir(int status, char *key, int keylen, char *val, int vallen, char *dummy)
-{
- char *p, *d, *de;
-
- if (status != YP_TRUE)
- return 1;
-
- if (vallen > keylen && *(p = val + keylen) == ':') {
- *p++ = '\0';
- for (de = val + vallen - 1; *de != ':' && de > val; de--);
- if (de > val) {
- *de = '\0';
- if ((d = strrchr(p, ':'))) {
- if (*++d && val[0])
- adduserdir(val, d, ND_USERNAME, 1);
- }
- }
- }
- return 0;
-}
-# endif /* HAVE_NIS */
-#endif /* HAVE_NIS_PLUS */
-
/**/
static void
fillnameddirtable(UNUSED(HashTable ht))
{
if (!allusersadded) {
-#if defined(HAVE_NIS) || defined(HAVE_NIS_PLUS)
- FILE *pwf;
- char buf[BUFSIZ], *p, *d, *de;
- int skipping, oldct = nameddirtab->ct, usepwf = 1;
-
-# ifndef HAVE_NIS_PLUS
- char domain[YPMAXDOMAIN];
- struct ypall_callback cb;
-
- /* Get potential matches from NIS and cull those without local accounts */
- if (getdomainname(domain, YPMAXDOMAIN) == 0) {
- cb.foreach = (int (*)()) add_userdir;
- cb.data = NULL;
- yp_all(domain, PASSWD_MAP, &cb);
- }
-# else /* HAVE_NIS_PLUS */
- /* Maybe we should turn this string into a #define'd constant...? */
-
- nis_list("passwd.org_dir", EXPAND_NAME|ALL_RESULTS|FOLLOW_LINKS|FOLLOW_PATH,
- add_userdir, 0);
-# endif
- if (nameddirtab->ct == oldct) {
- /* Using NIS or NIS+ didn't add any user directories. This seems
- * fishy, so we fall back to using getpwent(). If we don't have
- * that, we only use the passwd file. */
-#ifdef HAVE_GETPWENT
- struct passwd *pw;
-
- setpwent();
-
- /* loop through the password file/database *
- * and add all entries returned. */
- while ((pw = getpwent()) && !errflag)
- adduserdir(pw->pw_name, pw->pw_dir, ND_USERNAME, 1);
-
- endpwent();
- usepwf = 0;
-#endif /* HAVE_GETPWENT */
- }
- if (usepwf) {
- /* Don't forget the non-NIS matches from the flat passwd file */
- if ((pwf = fopen(PASSWD_FILE, "r")) != NULL) {
- skipping = 0;
- while (fgets(buf, BUFSIZ, pwf) != NULL) {
- if (strchr(buf, '\n') != NULL) {
- if (!skipping) {
- if ((p = strchr(buf, ':')) != NULL) {
- *p++ = '\0';
- if ((de = strrchr(p, ':'))) {
- *de = '\0';
- if ((d = strrchr(p, ':'))) {
- if (*++d && buf[0])
- adduserdir(buf, d, ND_USERNAME, 1);
- }
- }
- }
- } else
- skipping = 0;
- } else
- skipping = 1;
- }
- fclose(pwf);
- }
- }
-#else /* no NIS or NIS_PLUS */
#ifdef USE_GETPWENT
struct passwd *pw;
@@ -229,8 +107,7 @@ fillnameddirtable(UNUSED(HashTable ht))
adduserdir(pw->pw_name, pw->pw_dir, ND_USERNAME, 1);
endpwent();
-#endif /* HAVE_GETPWENT */
-#endif
+#endif /* USE_GETPWENT */
allusersadded = 1;
}
}
diff --git a/Src/hashtable.c b/Src/hashtable.c
index e210ddeca..bb165505e 100644
--- a/Src/hashtable.c
+++ b/Src/hashtable.c
@@ -28,25 +28,29 @@
*/
#include "../config.h"
+#include "zsh.mdh"
+#include "hashtable.pro"
+
+typedef struct scanstatus *ScanStatus;
+typedef struct hashtableimpl* HashTableImpl;
+
+struct hashtableimpl {
+ /* Public part of hash table, accessible from outside of hashtable.c. *
+ * Must be the first field to allow casting HashTable to HashTableImpl. */
+ struct hashtable pub;
+
+ /* HASHTABLE INTERNAL MEMBERS */
+ ScanStatus scan; /* status of a scan over this hashtable */
#ifdef ZSH_HASH_DEBUG
-# define HASHTABLE_DEBUG_MEMBERS \
- /* Members of struct hashtable used for debugging hash tables */ \
- HashTable next, last; /* linked list of all hash tables */ \
- char *tablename; /* string containing name of the hash table */ \
+ /* HASHTABLE DEBUG MEMBERS */
+ HashTableImpl next, last; /* linked list of all hash tables */
+ char *tablename; /* string containing name of the hash table */
PrintTableStats printinfo; /* pointer to function to print table stats */
-#else /* !ZSH_HASH_DEBUG */
-# define HASHTABLE_DEBUG_MEMBERS
#endif /* !ZSH_HASH_DEBUG */
+};
-#define HASHTABLE_INTERNAL_MEMBERS \
- ScanStatus scan; /* status of a scan over this hashtable */ \
- HASHTABLE_DEBUG_MEMBERS
-
-typedef struct scanstatus *ScanStatus;
-
-#include "zsh.mdh"
-#include "hashtable.pro"
+static inline HashTableImpl impl(HashTable ht) { return (HashTableImpl)ht; }
/* Structure for recording status of a hashtable scan in progress. When a *
* scan starts, the .scan member of the hashtable structure points to one *
@@ -71,7 +75,8 @@ struct scanstatus {
/********************************/
#ifdef ZSH_HASH_DEBUG
-static HashTable firstht, lastht;
+static void printhashtabinfo(HashTable ht);
+static HashTableImpl firstht, lastht;
#endif /* ZSH_HASH_DEBUG */
/* Generic hash function */
@@ -94,9 +99,9 @@ hasher(const char *str)
mod_export HashTable
newhashtable(int size, UNUSED(char const *name), UNUSED(PrintTableStats printinfo))
{
- HashTable ht;
+ HashTableImpl ht;
- ht = (HashTable) zshcalloc(sizeof *ht);
+ ht = (HashTableImpl) zshcalloc(sizeof *ht);
#ifdef ZSH_HASH_DEBUG
ht->next = NULL;
if(!firstht)
@@ -108,12 +113,12 @@ newhashtable(int size, UNUSED(char const *name), UNUSED(PrintTableStats printinf
ht->printinfo = printinfo ? printinfo : printhashtabinfo;
ht->tablename = ztrdup(name);
#endif /* ZSH_HASH_DEBUG */
- ht->nodes = (HashNode *) zshcalloc(size * sizeof(HashNode));
- ht->hsize = size;
- ht->ct = 0;
+ ht->pub.nodes = (HashNode *) zshcalloc(size * sizeof(HashNode));
+ ht->pub.hsize = size;
+ ht->pub.ct = 0;
ht->scan = NULL;
- ht->scantab = NULL;
- return ht;
+ ht->pub.scantab = NULL;
+ return &ht->pub;
}
/* Delete a hash table. After this function has been used, any *
@@ -125,18 +130,18 @@ deletehashtable(HashTable ht)
{
ht->emptytable(ht);
#ifdef ZSH_HASH_DEBUG
- if(ht->next)
- ht->next->last = ht->last;
+ if(impl(ht)->next)
+ impl(ht)->next->last = impl(ht)->last;
else
- lastht = ht->last;
- if(ht->last)
- ht->last->next = ht->next;
+ lastht = impl(ht)->last;
+ if(impl(ht)->last)
+ impl(ht)->last->next = impl(ht)->next;
else
- firstht = ht->next;
- zsfree(ht->tablename);
+ firstht = impl(ht)->next;
+ zsfree(impl(ht)->tablename);
#endif /* ZSH_HASH_DEBUG */
zfree(ht->nodes, ht->hsize * sizeof(HashNode));
- zfree(ht, sizeof(*ht));
+ zfree(ht, sizeof(struct hashtableimpl));
}
/* Add a node to a hash table. *
@@ -175,7 +180,7 @@ addhashnode2(HashTable ht, char *nam, void *nodeptr)
if (!hp) {
hn->next = NULL;
ht->nodes[hashval] = hn;
- if (++ht->ct >= ht->hsize * 2 && !ht->scan)
+ if (++ht->ct >= ht->hsize * 2 && !impl(ht)->scan)
expandhashtable(ht);
return NULL;
}
@@ -185,15 +190,15 @@ addhashnode2(HashTable ht, char *nam, void *nodeptr)
ht->nodes[hashval] = hn;
replacing:
hn->next = hp->next;
- if(ht->scan) {
- if(ht->scan->sorted) {
- HashNode *hashtab = ht->scan->u.s.hashtab;
+ if(impl(ht)->scan) {
+ if(impl(ht)->scan->sorted) {
+ HashNode *hashtab = impl(ht)->scan->u.s.hashtab;
int i;
- for(i = ht->scan->u.s.ct; i--; )
+ for(i = impl(ht)->scan->u.s.ct; i--; )
if(hashtab[i] == hp)
hashtab[i] = hn;
- } else if(ht->scan->u.u == hp)
- ht->scan->u.u = hn;
+ } else if(impl(ht)->scan->u.u == hp)
+ impl(ht)->scan->u.u = hn;
}
return hp;
}
@@ -211,7 +216,7 @@ addhashnode2(HashTable ht, char *nam, void *nodeptr)
/* else just add it at the front of the list */
hn->next = ht->nodes[hashval];
ht->nodes[hashval] = hn;
- if (++ht->ct >= ht->hsize * 2 && !ht->scan)
+ if (++ht->ct >= ht->hsize * 2 && !impl(ht)->scan)
expandhashtable(ht);
return NULL;
}
@@ -284,15 +289,15 @@ removehashnode(HashTable ht, const char *nam)
ht->nodes[hashval] = hp->next;
gotit:
ht->ct--;
- if(ht->scan) {
- if(ht->scan->sorted) {
- HashNode *hashtab = ht->scan->u.s.hashtab;
+ if(impl(ht)->scan) {
+ if(impl(ht)->scan->sorted) {
+ HashNode *hashtab = impl(ht)->scan->u.s.hashtab;
int i;
- for(i = ht->scan->u.s.ct; i--; )
+ for(i = impl(ht)->scan->u.s.ct; i--; )
if(hashtab[i] == hp)
hashtab[i] = NULL;
- } else if(ht->scan->u.u == hp)
- ht->scan->u.u = hp->next;
+ } else if(impl(ht)->scan->u.u == hp)
+ impl(ht)->scan->u.u = hp->next;
}
return hp;
}
@@ -399,7 +404,7 @@ scanmatchtable(HashTable ht, Patprog pprog, int sorted,
st.sorted = 1;
st.u.s.hashtab = hnsorttab;
st.u.s.ct = ct;
- ht->scan = &st;
+ impl(ht)->scan = &st;
for (htp = hnsorttab, i = 0; i < ct; i++, htp++) {
if ((!flags1 || ((*htp)->flags & flags1)) &&
@@ -410,13 +415,13 @@ scanmatchtable(HashTable ht, Patprog pprog, int sorted,
}
}
- ht->scan = NULL;
+ impl(ht)->scan = NULL;
} else {
int i, hsize = ht->hsize;
HashNode *nodes = ht->nodes;
st.sorted = 0;
- ht->scan = &st;
+ impl(ht)->scan = &st;
for (i = 0; i < hsize; i++)
for (st.u.u = nodes[i]; st.u.u; ) {
@@ -429,7 +434,7 @@ scanmatchtable(HashTable ht, Patprog pprog, int sorted,
}
}
- ht->scan = NULL;
+ impl(ht)->scan = NULL;
}
return match;
@@ -531,7 +536,7 @@ printhashtabinfo(HashTable ht)
int chainlen[MAXDEPTH + 1];
int i, tmpcount, total;
- printf("name of table : %s\n", ht->tablename);
+ printf("name of table : %s\n", impl(ht)->tablename);
printf("size of nodes[] : %d\n", ht->hsize);
printf("number of nodes : %d\n\n", ht->ct);
@@ -560,12 +565,12 @@ printhashtabinfo(HashTable ht)
int
bin_hashinfo(UNUSED(char *nam), UNUSED(char **args), UNUSED(Options ops), UNUSED(int func))
{
- HashTable ht;
+ HashTableImpl ht;
printf("----------------------------------------------------\n");
queue_signals();
for(ht = firstht; ht; ht = ht->next) {
- ht->printinfo(ht);
+ ht->printinfo(&ht->pub);
printf("----------------------------------------------------\n");
}
unqueue_signals();
diff --git a/Src/hist.c b/Src/hist.c
index 5281e8718..f9440dba7 100644
--- a/Src/hist.c
+++ b/Src/hist.c
@@ -339,6 +339,13 @@ hist_in_word(int yesno)
histactive &= ~HA_INWORD;
}
+/**/
+int
+hist_is_in_word(void)
+{
+ return (histactive & HA_INWORD) ? 1 : 0;
+}
+
/* add a character to the current history word */
static void
@@ -842,7 +849,7 @@ histsubchar(int c)
break;
case 'A':
- if (!chrealpath(&sline)) {
+ if (!chrealpath(&sline, 'A', 1)) {
herrflush();
zerr("modifier failed: A");
return -1;
@@ -1854,7 +1861,11 @@ chabspath(char **junkptr)
return 1;
if (**junkptr != '/') {
- *junkptr = zhtricat(metafy(zgetcwd(), -1, META_HEAPDUP), "/", *junkptr);
+ char *here = zgetcwd();
+ if (here[strlen(here)-1] != '/')
+ *junkptr = zhtricat(metafy(here, -1, META_HEAPDUP), "/", *junkptr);
+ else
+ *junkptr = dyncat(here, *junkptr);
}
current = *junkptr;
@@ -1922,9 +1933,20 @@ chabspath(char **junkptr)
return 1;
}
+/*
+ * Resolve symlinks in junkptr.
+ *
+ * If mode is 'A', resolve dot-dot before symlinks. Else, mode should be 'P'.
+ * Refer to the documentation of the :A and :P modifiers for details.
+ *
+ * use_heap is 1 if the result is to be allocated on the heap, 0 otherwise.
+ *
+ * Return 0 for error, non-zero for success.
+ */
+
/**/
int
-chrealpath(char **junkptr)
+chrealpath(char **junkptr, char mode, int use_heap)
{
char *str;
#ifdef HAVE_REALPATH
@@ -1936,12 +1958,14 @@ chrealpath(char **junkptr)
# endif
#endif
+ DPUTS1(mode != 'A' && mode != 'P', "chrealpath: mode='%c' is invalid", mode);
+
if (!**junkptr)
return 1;
- /* Notice that this means ..'s are applied before symlinks are resolved! */
- if (!chabspath(junkptr))
- return 0;
+ if (mode == 'A')
+ if (!chabspath(junkptr))
+ return 0;
#ifndef HAVE_REALPATH
return 1;
@@ -1989,14 +2013,15 @@ chrealpath(char **junkptr)
str++;
}
+ use_heap = (use_heap ? META_HEAPDUP : META_DUP);
if (real) {
- *junkptr = metafy(str = bicat(real, nonreal), -1, META_HEAPDUP);
+ *junkptr = metafy(str = bicat(real, nonreal), -1, use_heap);
zsfree(str);
#ifdef REALPATH_ACCEPTS_NULL
free(real);
#endif
} else {
- *junkptr = metafy(nonreal, lastpos - nonreal + 1, META_HEAPDUP);
+ *junkptr = metafy(nonreal, lastpos - nonreal + 1, use_heap);
}
#endif
@@ -2227,6 +2252,7 @@ casemodify(char *str, int how)
#endif
while (*str) {
int c;
+ int mod = 0;
if (*str == Meta) {
c = str[1] ^ 32;
str += 2;
@@ -2234,13 +2260,17 @@ casemodify(char *str, int how)
c = *str++;
switch (how) {
case CASMOD_LOWER:
- if (isupper(c))
+ if (isupper(c)) {
c = tolower(c);
+ mod = 1;
+ }
break;
case CASMOD_UPPER:
- if (islower(c))
+ if (islower(c)) {
c = toupper(c);
+ mod = 1;
+ }
break;
case CASMOD_CAPS:
@@ -2248,14 +2278,18 @@ casemodify(char *str, int how)
if (!ialnum(c))
nextupper = 1;
else if (nextupper) {
- if (islower(c))
+ if (islower(c)) {
c = toupper(c);
+ mod = 1;
+ }
nextupper = 0;
- } else if (isupper(c))
+ } else if (isupper(c)) {
c = tolower(c);
+ mod = 1;
+ }
break;
}
- if (imeta(c)) {
+ if (mod && imeta(c)) {
*ptr2++ = Meta;
*ptr2++ = c ^ 32;
} else
@@ -2575,11 +2609,13 @@ resizehistents(void)
}
static int
-readhistline(int start, char **bufp, int *bufsiz, FILE *in)
+readhistline(int start, char **bufp, int *bufsiz, FILE *in, int *readbytes)
{
char *buf = *bufp;
if (fgets(buf + start, *bufsiz - start, in)) {
- int len = start + strlen(buf + start);
+ int len = strlen(buf + start);
+ *readbytes += len;
+ len += start;
if (len == start)
return -1;
if (buf[len - 1] != '\n') {
@@ -2588,16 +2624,23 @@ readhistline(int start, char **bufp, int *bufsiz, FILE *in)
return -1;
*bufp = zrealloc(buf, 2 * (*bufsiz));
*bufsiz = 2 * (*bufsiz);
- return readhistline(len, bufp, bufsiz, in);
+ return readhistline(len, bufp, bufsiz, in, readbytes);
}
}
else {
+ int spc;
buf[len - 1] = '\0';
if (len > 1 && buf[len - 2] == '\\') {
buf[--len - 1] = '\n';
if (!feof(in))
- return readhistline(len, bufp, bufsiz, in);
+ return readhistline(len, bufp, bufsiz, in, readbytes);
}
+
+ spc = len - 2;
+ while (spc >= 0 && buf[spc] == ' ')
+ spc--;
+ if (spc != len - 2 && buf[spc] == '\\')
+ buf[--len - 1] = '\0';
}
return len;
}
@@ -2616,7 +2659,7 @@ readhistfile(char *fn, int err, int readflags)
short *words;
struct stat sb;
int nwordpos, nwords, bufsiz;
- int searching, newflags, l, ret, uselex;
+ int searching, newflags, l, ret, uselex, readbytes;
if (!fn && !(fn = getsparam("HISTFILE")))
return;
@@ -2648,7 +2691,7 @@ readhistfile(char *fn, int err, int readflags)
pushheap();
if (readflags & HFILE_FAST && lasthist.text) {
if (lasthist.fpos < lasthist.fsiz) {
- fseek(in, lasthist.fpos, 0);
+ fseek(in, lasthist.fpos, SEEK_SET);
searching = 1;
}
else {
@@ -2658,13 +2701,15 @@ readhistfile(char *fn, int err, int readflags)
} else
searching = 0;
+ fpos = ftell(in);
+ readbytes = 0;
newflags = HIST_OLD | HIST_READ;
if (readflags & HFILE_FAST)
newflags |= HIST_FOREIGN;
if (readflags & HFILE_SKIPOLD
|| (hist_ignore_all_dups && newflags & hist_skip_flags))
newflags |= HIST_MAKEUNIQUE;
- while (fpos = ftell(in), (l = readhistline(0, &buf, &bufsiz, in))) {
+ while (fpos += readbytes, readbytes = 0, (l = readhistline(0, &buf, &bufsiz, in, &readbytes))) {
char *pt;
int remeta = 0;
@@ -2723,7 +2768,7 @@ readhistfile(char *fn, int err, int readflags)
&& histstrcmp(pt, lasthist.text) == 0)
searching = 0;
else {
- fseek(in, 0, 0);
+ fseek(in, 0, SEEK_SET);
histfile_linect = 0;
searching = -1;
}
@@ -2959,7 +3004,7 @@ savehistfile(char *fn, int err, int writeflags)
ret = 0;
for (; he && he->histnum <= xcurhist; he = down_histent(he)) {
- int count_backslashes = 0;
+ int end_backslashes = 0;
if ((writeflags & HFILE_SKIPDUPS && he->node.flags & HIST_DUP)
|| (writeflags & HFILE_SKIPFOREIGN && he->node.flags & HIST_FOREIGN)
@@ -2992,18 +3037,14 @@ savehistfile(char *fn, int err, int writeflags)
if (*t == '\n')
if ((ret = fputc('\\', out)) < 0)
break;
- if (*t == '\\')
- count_backslashes++;
- else
- count_backslashes = 0;
+ end_backslashes = (*t == '\\' || (end_backslashes && *t == ' '));
if ((ret = fputc(*t, out)) < 0)
break;
}
if (ret < 0)
break;
- if (count_backslashes && (count_backslashes % 2 == 0))
- if ((ret = fputc(' ', out)) < 0)
- break;
+ if (end_backslashes)
+ ret = fputc(' ', out);
if (ret < 0 || (ret = fputc('\n', out)) < 0)
break;
}
diff --git a/Src/init.c b/Src/init.c
index 99ccc16cf..871d46b12 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -248,7 +248,8 @@ static int restricted;
/**/
static void
-parseargs(char *zsh_name, char **argv, char **runscript, char **cmdptr)
+parseargs(char *zsh_name, char **argv, char **runscript, char **cmdptr,
+ int *needkeymap)
{
char **x;
LinkList paramlist;
@@ -265,7 +266,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))
+ if (parseopts(zsh_name, &argv, opts, cmdptr, NULL, flags, needkeymap))
exit(1);
/*
@@ -376,7 +377,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)
+ LinkList optlist, int flags, int *needkeymap)
{
int optionbreak = 0;
int action, optno;
@@ -482,8 +483,14 @@ 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) {
- WARN_OPTION("can't change option: %s", *argv);
+ } 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 (dosetopt(optno, action, toplevel, new_opts) &&
!toplevel) {
@@ -1035,7 +1042,6 @@ setupvals(char *cmd, char *runscript, char *zsh_name)
#endif /* FPATH_NEEDS_INIT */
mailpath = mkarray(NULL);
- watch = mkarray(NULL);
psvar = mkarray(NULL);
module_path = mkarray(ztrdup(MODULE_DIR));
modulestab = newmoduletable(17, "modules");
@@ -1705,7 +1711,7 @@ zsh_main(UNUSED(int argc), char **argv)
{
char **t, *runscript = NULL, *zsh_name;
char *cmd; /* argument to -c */
- int t0;
+ int t0, needkeymap = 0;
#ifdef USE_LOCALE
setlocale(LC_ALL, "");
#endif
@@ -1751,7 +1757,7 @@ zsh_main(UNUSED(int argc), char **argv)
createoptiontable();
/* sets emulation, LOGINSHELL, PRIVILEGED, ZLE, INTERACTIVE,
* SHINSTDIN and SINGLECOMMAND */
- parseargs(zsh_name, argv, &runscript, &cmd);
+ parseargs(zsh_name, argv, &runscript, &cmd, &needkeymap);
SHTTY = -1;
init_io(cmd);
@@ -1760,6 +1766,15 @@ 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 18228b37d..c59232681 100644
--- a/Src/input.c
+++ b/Src/input.c
@@ -223,13 +223,20 @@ shingetchar(void)
return STOUC(*shinbufptr++);
shinbufreset();
- do {
- errno = 0;
- nread = read(SHIN, shinbuffer, SHINBUFSIZE);
- } while (nread < 0 && errno == EINTR);
- if (nread <= 0)
- return -1;
- shinbufendptr = shinbuffer + nread;
+ for (;;) {
+ errno = 0;
+ nread = read(SHIN, shinbufendptr, 1);
+ if (nread > 0) {
+ /* Use line buffering (POSIX requirement) */
+ if (*shinbufendptr++ == '\n')
+ break;
+ if (shinbufendptr == shinbuffer + SHINBUFSIZE)
+ break;
+ } else if (nread == 0 || errno != EINTR)
+ break;
+ }
+ if (shinbufendptr == shinbuffer)
+ return -1;
return STOUC(*shinbufptr++);
}
@@ -595,9 +602,9 @@ stuff(char *fn)
zerr("can't open %s", fn);
return 1;
}
- fseek(in, 0, 2);
+ fseek(in, 0, SEEK_END);
len = ftell(in);
- fseek(in, 0, 0);
+ fseek(in, 0, SEEK_SET);
buf = (char *)zalloc(len + 1);
if (!(fread(buf, len, 1, in))) {
zerr("read error on %s", fn);
diff --git a/Src/jobs.c b/Src/jobs.c
index e7438251e..af0a1233d 100644
--- a/Src/jobs.c
+++ b/Src/jobs.c
@@ -98,10 +98,12 @@ mod_export int jobtabsize;
mod_export int maxjob;
/* If we have entered a subshell, the original shell's job table. */
-static struct job *oldjobtab;
+/**/
+mod_export struct job *oldjobtab;
/* The size of that. */
-static int oldmaxjob;
+/**/
+mod_export int oldmaxjob;
/* shell timings */
@@ -283,7 +285,8 @@ handle_sub(int job, int fg)
if ((cp = ((WIFEXITED(jn->procs->status) ||
WIFSIGNALED(jn->procs->status)) &&
- killpg(jn->gleader, 0) == -1))) {
+ (killpg(jn->gleader, 0) == -1 &&
+ errno == ESRCH)))) {
Process p;
for (p = jn->procs; p->next; p = p->next);
jn->gleader = p->pid;
@@ -441,7 +444,7 @@ update_job(Job jn)
Process pn;
int job;
int val = 0, status = 0;
- int somestopped = 0, inforeground = 0;
+ int somestopped = 0, inforeground = 0, signalled = 0;
for (pn = jn->auxprocs; pn; pn = pn->next) {
#ifdef WIFCONTINUED
@@ -463,12 +466,15 @@ update_job(Job jn)
return; /* so no need to update job table entry */
if (WIFSTOPPED(pn->status)) /* some processes are stopped */
somestopped = 1; /* so job is not done, but entry needs updating */
- if (!pn->next) /* last job in pipeline determines exit status */
+ if (!pn->next) {
+ /* last job in pipeline determines exit status */
val = (WIFSIGNALED(pn->status) ?
0200 | WTERMSIG(pn->status) :
(WIFSTOPPED(pn->status) ?
0200 | WEXITSTATUS(pn->status) :
WEXITSTATUS(pn->status)));
+ signalled = WIFSIGNALED(pn->status);
+ }
if (pn->pid == jn->gleader) /* if this process is process group leader */
status = pn->status;
}
@@ -541,9 +547,13 @@ update_job(Job jn)
/* is this job in the foreground of an interactive shell? */
if (mypgrp != pgrp && inforeground &&
- (jn->gleader == pgrp || (pgrp > 1 && kill(-pgrp, 0) == -1))) {
+ (jn->gleader == pgrp ||
+ (pgrp > 1 &&
+ (kill(-pgrp, 0) == -1 && errno == ESRCH)))) {
if (list_pipe) {
- if (somestopped || (pgrp > 1 && kill(-pgrp, 0) == -1)) {
+ if (somestopped || (pgrp > 1 &&
+ kill(-pgrp, 0) == -1 &&
+ errno == ESRCH)) {
attachtty(mypgrp);
/* check window size and adjust if necessary */
adjustwinsize(0);
@@ -559,7 +569,7 @@ update_job(Job jn)
}
/* If we have `foo|while true; (( x++ )); done', and hit
* ^C, we have to stop the loop, too. */
- if ((val & 0200) && inforeground == 1 &&
+ if (signalled && inforeground == 1 &&
((val & ~0200) == SIGINT || (val & ~0200) == SIGQUIT)) {
if (!errbrk_saved) {
errbrk_saved = 1;
@@ -576,7 +586,7 @@ update_job(Job jn)
adjustwinsize(0);
}
}
- } else if (list_pipe && (val & 0200) && inforeground == 1 &&
+ } else if (list_pipe && signalled && inforeground == 1 &&
((val & ~0200) == SIGINT || (val & ~0200) == SIGQUIT)) {
if (!errbrk_saved) {
errbrk_saved = 1;
@@ -1708,8 +1718,15 @@ clearjobtab(int monitor)
/* Don't report any job we're part of */
if (thisjob != -1 && thisjob < oldmaxjob)
memset(oldjobtab+thisjob, 0, sizeof(struct job));
+
+ /* oldmaxjob is now the size of the table, but outside
+ * this function, it's used as a job number, which must
+ * be the largest index available in the table.
+ */
+ --oldmaxjob;
}
+
memset(jobtab, 0, jobtabsize * sizeof(struct job)); /* zero out table */
maxjob = 0;
@@ -1723,6 +1740,18 @@ clearjobtab(int monitor)
thisjob = initjob();
}
+/* In a subshell, decide we want our own job table after all. */
+
+/**/
+mod_export void
+clearoldjobtab(void)
+{
+ if (oldjobtab)
+ free(oldjobtab);
+ oldjobtab = NULL;
+ oldmaxjob = 0;
+}
+
static int initnewjob(int i)
{
jobtab[i].stat = STAT_INUSE;
@@ -1854,13 +1883,14 @@ scanjobs(void)
/* This simple function indicates whether or not s may represent *
* a number. It returns true iff s consists purely of digits and *
- * minuses. Note that minus may appear more than once, and the empty *
- * string will produce a `true' response. */
+ * minuses. Note that minus may appear more than once. */
/**/
static int
isanum(char *s)
{
+ if (*s == '\0')
+ return 0;
while (*s == '-' || idigit(*s))
s++;
return *s == '\0';
@@ -1885,6 +1915,26 @@ setcurjob(void)
}
}
+/* Find the job table for reporting jobs */
+
+/**/
+mod_export void
+selectjobtab(Job *jtabp, int *jmaxp)
+{
+ if (oldjobtab)
+ {
+ /* In subshell --- use saved job table to report */
+ *jtabp = oldjobtab;
+ *jmaxp = oldmaxjob;
+ }
+ else
+ {
+ /* Use main job table */
+ *jtabp = jobtab;
+ *jmaxp = maxjob;
+ }
+}
+
/* Convert a job specifier ("%%", "%1", "%foo", "%?bar?", etc.) *
* to a job number. */
@@ -1895,13 +1945,7 @@ getjob(const char *s, const char *prog)
int jobnum, returnval, mymaxjob;
Job myjobtab;
- if (oldjobtab) {
- myjobtab = oldjobtab;
- mymaxjob = oldmaxjob;
- } else {
- myjobtab= jobtab;
- mymaxjob = maxjob;
- }
+ selectjobtab(&myjobtab, &mymaxjob);
/* if there is no %, treat as a name */
if (*s != '%')
@@ -2261,6 +2305,13 @@ bin_fg(char *name, char **argv, Options ops, int func)
memcpy(hackzero, *argv, len);
memset(hackzero + len, 0, hackspace - len);
#endif
+
+#ifdef HAVE_PRCTL
+ /* try to change /proc/$$/comm which will *
+ * be used when checking with "ps -e" */
+#include <sys/prctl.h>
+ prctl(PR_SET_NAME, *argv);
+#endif
unqueue_signals();
return 0;
}
@@ -2417,6 +2468,7 @@ bin_fg(char *name, char **argv, Options ops, int func)
case BIN_BG:
case BIN_WAIT:
if (func == BIN_BG) {
+ clearoldjobtab();
jobtab[job].stat |= STAT_NOSTTY;
jobtab[job].stat &= ~STAT_CURSH;
}
@@ -2469,7 +2521,8 @@ bin_fg(char *name, char **argv, Options ops, int func)
if ((jobtab[job].stat & STAT_SUPERJOB) &&
((!jobtab[job].procs->next ||
(jobtab[job].stat & STAT_SUBLEADER) ||
- killpg(jobtab[job].gleader, 0) == -1)) &&
+ (killpg(jobtab[job].gleader, 0) == -1 &&
+ errno == ESRCH))) &&
jobtab[jobtab[job].other].gleader)
attachtty(jobtab[jobtab[job].other].gleader);
else
diff --git a/Src/lex.c b/Src/lex.c
index 1d86da94e..ece02659e 100644
--- a/Src/lex.c
+++ b/Src/lex.c
@@ -270,7 +270,7 @@ zshlex(void)
do {
if (inrepeat_)
++inrepeat_;
- if (inrepeat_ == 3 && isset(SHORTLOOPS))
+ if (inrepeat_ == 3 && (isset(SHORTLOOPS) || isset(SHORTREPEAT)))
incmdpos = 1;
tok = gettok();
} while (tok != ENDINPUT && exalias());
@@ -541,6 +541,17 @@ cmd_or_math_sub(void)
{
int c = hgetc(), ret;
+ if (c == '\\') {
+ c = hgetc();
+ if (c != '\n') {
+ hungetc(c);
+ hungetc('\\');
+ lexstop = 0;
+ return skipcomm() ? CMD_OR_MATH_ERR : CMD_OR_MATH_CMD;
+ }
+ c = hgetc();
+ }
+
if (c == '(') {
int lexpos = (int)(lexbuf.ptr - tokstr);
add(Inpar);
@@ -998,6 +1009,16 @@ gettokstr(int c, int sub)
break;
case LX2_STRING:
e = hgetc();
+ if (e == '\\') {
+ e = hgetc();
+ if (e != '\n') {
+ hungetc(e);
+ hungetc('\\');
+ lexstop = 0;
+ break;
+ }
+ e = hgetc();
+ }
if (e == '[') {
cmdpush(CS_MATHSUBST);
add(String);
@@ -1868,6 +1889,7 @@ exalias(void)
hwend();
if (interact && isset(SHINSTDIN) && !strin && incasepat <= 0 &&
tok == STRING && !nocorrect && !(inbufflags & INP_ALIAS) &&
+ !hist_is_in_word() &&
(isset(CORRECTALL) || (isset(CORRECT) && incmdpos)))
spckword(&tokstr, 1, incmdpos, 1);
diff --git a/Src/linklist.c b/Src/linklist.c
index 85d9bb367..f64685d9e 100644
--- a/Src/linklist.c
+++ b/Src/linklist.c
@@ -438,22 +438,27 @@ hlinklist2array(LinkList list, int copy)
/*
* Convert a linked list whose data elements are strings to
- * an array. The result is a permanently allocated, freearrayable
- * array.
+ * a permanently-allocated array. The elements of the array are the same
+ * elements as the linked list data if copy is 0, else they are duplicated
+ * into permanent memory so the result is a permanently allocated,
+ * freearrayable array that's a deep copy of the linked list.
*/
/**/
mod_export char **
-zlinklist2array(LinkList list)
+zlinklist2array(LinkList list, int copy)
{
int l = countlinknodes(list);
char **ret = (char **) zalloc((l + 1) * sizeof(char *)), **p;
LinkNode n;
for (n = firstnode(list), p = ret; n; incnode(n), p++) {
- *p = ztrdup((char *) getdata(n));
+ *p = (char *) getdata(n);
+ if (copy)
+ *p = ztrdup(*p);
}
*p = NULL;
return ret;
}
+
diff --git a/Src/loop.c b/Src/loop.c
index 01abc6cc9..db5b3e097 100644
--- a/Src/loop.c
+++ b/Src/loop.c
@@ -43,7 +43,7 @@ mod_export int contflag;
/* # of break levels */
/**/
-mod_export int breaks;
+mod_export volatile int breaks;
/**/
int
@@ -497,13 +497,15 @@ execrepeat(Estate state, UNUSED(int do_exec))
end = state->pc + WC_REPEAT_SKIP(code);
- lastval = 0;
tmp = ecgetstr(state, EC_DUPTOK, &htok);
- if (htok)
+ if (htok) {
singsub(&tmp);
+ untokenize(tmp);
+ }
count = mathevali(tmp);
if (errflag)
return 1;
+ lastval = 0; /* used when the repeat count is zero */
pushheap();
cmdpush(CS_REPEAT);
loops++;
@@ -581,7 +583,7 @@ execif(Estate state, int do_exec)
cmdpop();
} else {
noerrexit = olderrexit;
- if (!retflag)
+ if (!retflag && !errflag)
lastval = 0;
}
state->pc = end;
@@ -742,7 +744,7 @@ exectry(Estate state, int do_exec)
/* The :try clause */
++try_tryflag;
- execlist(state, 1, do_exec);
+ execlist(state, 1, 0);
--try_tryflag;
/* Don't record errflag here, may be reset. However, */
diff --git a/Src/makepro.awk b/Src/makepro.awk
index 226d3f96b..f69660531 100644
--- a/Src/makepro.awk
+++ b/Src/makepro.awk
@@ -79,7 +79,7 @@ BEGIN {
break
}
sub(/^ */, "", line)
- match(line, /^((const|enum|mod_export|static|struct|union) +)*([_0-9A-Za-z]+ +|((char|double|float|int|long|short|unsigned|void) +)+)((const|static) +)*/)
+ match(line, /^((const|enum|mod_export|static|struct|union|volatile) +)*([_0-9A-Za-z]+ +|((char|double|float|int|long|short|unsigned|void) +)+)((const|static) +)*/)
dtype = substr(line, 1, RLENGTH)
sub(/ *$/, "", dtype)
if(" " dtype " " ~ / static /)
diff --git a/Src/math.c b/Src/math.c
index 905b910ec..777ad9c31 100644
--- a/Src/math.c
+++ b/Src/math.c
@@ -162,7 +162,7 @@ static int unary = 1;
#define TOKCOUNT 53
/*
- * Opeator recedences: in reverse order, i.e. lower number, high precedence.
+ * Operator precedences: in reverse order, i.e. lower number, high precedence.
* These are the C precedences.
*
* 0 Non-operators: NUM (numeric constant), ID (identifier),
@@ -219,7 +219,7 @@ static int c_prec[TOKCOUNT] =
};
/*
- * Opeator recedences: in reverse order, i.e. lower number, high precedence.
+ * Operator precedences: in reverse order, i.e. lower number, high precedence.
* These are the default zsh precedences.
*
* 0 Non-operators: NUM (numeric constant), ID (identifier),
@@ -831,6 +831,8 @@ zzlex(void)
case ' ': /* Fall through! */
case '\t':
case '\n':
+ case '"': /* POSIX says ignore these */
+ case Dnull:
break;
default:
if (idigit(*--ptr) || *ptr == '.')
@@ -838,13 +840,18 @@ zzlex(void)
if (*ptr == '#') {
if (*++ptr == '\\' || *ptr == '#') {
int v;
+ char *optr = ptr;
ptr++;
if (!*ptr) {
zerr("bad math expression: character missing after ##");
return EOI;
}
- ptr = getkeystring(ptr, NULL, GETKEYS_MATH, &v);
+ if(!(ptr = getkeystring(ptr, NULL, GETKEYS_MATH, &v))) {
+ zerr("bad math expression: bad character after ##");
+ ptr = optr;
+ return EOI;
+ }
yyval.u.l = v;
return NUM;
}
@@ -856,14 +863,18 @@ zzlex(void)
p = ptr;
ptr = ie;
- if (ie - p == 3) {
- if (strncasecmp(p, "NaN", 3) == 0) {
+ if (ie - p == 3 && !EMULATION(EMULATE_SH)) {
+ if ((p[0] == 'N' || p[0] == 'n') &&
+ (p[1] == 'A' || p[1] == 'a') &&
+ (p[2] == 'N' || p[2] == 'n')) {
yyval.type = MN_FLOAT;
yyval.u.d = 0.0;
yyval.u.d /= yyval.u.d;
return NUM;
}
- else if (strncasecmp(p, "Inf", 3) == 0) {
+ else if ((p[0] == 'I' || p[0] == 'i') &&
+ (p[1] == 'N' || p[1] == 'n') &&
+ (p[2] == 'F' || p[2] == 'f')) {
yyval.type = MN_FLOAT;
yyval.u.d = 0.0;
yyval.u.d = 1.0 / yyval.u.d;
diff --git a/Src/mem.c b/Src/mem.c
index 5951e57ed..fb4be47bf 100644
--- a/Src/mem.c
+++ b/Src/mem.c
@@ -1072,19 +1072,6 @@ zrealloc(void *ptr, size_t size)
# endif
#endif
-#if defined(_BSD) && !defined(STDC_HEADERS)
-# define FREE_RET_T int
-# define FREE_ARG_T char *
-# define FREE_DO_RET
-# define MALLOC_RET_T char *
-# define MALLOC_ARG_T size_t
-#else
-# define FREE_RET_T void
-# define FREE_ARG_T void *
-# define MALLOC_RET_T void *
-# define MALLOC_ARG_T size_t
-#endif
-
/* structure for building free list in blocks holding small blocks */
struct m_shdr {
@@ -1198,8 +1185,8 @@ static struct m_hdr *m_l;
#endif /* ZSH_MEM_DEBUG */
-MALLOC_RET_T
-malloc(MALLOC_ARG_T size)
+void *
+malloc(size_t size)
{
struct m_hdr *m, *mp, *mt;
long n, s, os = 0;
@@ -1226,7 +1213,7 @@ malloc(MALLOC_ARG_T size)
#if 1
size = 1;
#else
- return (MALLOC_RET_T) m_high;
+ return (void *) m_high;
#endif
queue_signals(); /* just queue signals rather than handling them */
@@ -1284,7 +1271,7 @@ malloc(MALLOC_ARG_T size)
#endif
unqueue_signals();
- return (MALLOC_RET_T) sh;
+ return (void *) sh;
}
/* we still want a small block but there were no block with a free
small block of the requested size; so we use the real allocation
@@ -1426,14 +1413,14 @@ malloc(MALLOC_ARG_T size)
#endif
unqueue_signals();
- return (MALLOC_RET_T) (((char *)m) + sizeof(struct m_hdr));
+ return (void *) (((char *)m) + sizeof(struct m_hdr));
}
#ifdef ZSH_MEM_DEBUG
m_m[m->len < (1024 * M_ISIZE) ? (m->len / M_ISIZE) : 1024]++;
#endif
unqueue_signals();
- return (MALLOC_RET_T) & m->next;
+ return (void *) & m->next;
}
/* this is an internal free(); the second argument may, but need not hold
@@ -1640,14 +1627,10 @@ zfree(void *p, int sz)
unqueue_signals();
}
-FREE_RET_T
-free(FREE_ARG_T p)
+void
+free(void *p)
{
zfree(p, 0); /* 0 means: size is unknown */
-
-#ifdef FREE_DO_RET
- return 0;
-#endif
}
/* this one is for strings (and only strings, real strings, real C strings,
@@ -1661,8 +1644,8 @@ zsfree(char *p)
zfree(p, strlen(p) + 1);
}
-MALLOC_RET_T
-realloc(MALLOC_RET_T p, MALLOC_ARG_T size)
+void *
+realloc(void *p, size_t size)
{
struct m_hdr *m = (struct m_hdr *)(((char *)p) - M_ISIZE), *mt;
char *r;
@@ -1673,12 +1656,12 @@ realloc(MALLOC_RET_T p, MALLOC_ARG_T size)
queue_signals();
r = malloc(size);
unqueue_signals();
- return (MALLOC_RET_T) r;
+ return (void *) r;
}
/* and some systems even do this... */
if (!p || !size)
- return (MALLOC_RET_T) p;
+ return p;
queue_signals(); /* just queue signals caught rather than handling them */
@@ -1707,17 +1690,17 @@ realloc(MALLOC_RET_T p, MALLOC_ARG_T size)
free(p);
unqueue_signals();
- return (MALLOC_RET_T) r;
+ return (void *) r;
}
-MALLOC_RET_T
-calloc(MALLOC_ARG_T n, MALLOC_ARG_T size)
+void *
+calloc(size_t n, size_t size)
{
long l;
char *r;
if (!(l = n * size))
- return (MALLOC_RET_T) m_high;
+ return (void *) m_high;
/*
* use realloc() (with a NULL `p` argument it behaves exactly the same
@@ -1729,7 +1712,7 @@ calloc(MALLOC_ARG_T n, MALLOC_ARG_T size)
memset(r, 0, l);
- return (MALLOC_RET_T) r;
+ return (void *) r;
}
#ifdef ZSH_MEM_DEBUG
diff --git a/Src/module.c b/Src/module.c
index f41b82f25..bab4d8d73 100644
--- a/Src/module.c
+++ b/Src/module.c
@@ -1039,7 +1039,7 @@ checkaddparam(const char *nam, int opt_i)
* non-autoloadable parameter already there. This
* is consistent with the way add_auto* functions work.
*/
- if (!opt_i || !pm->level) {
+ if (!opt_i || pm->level) {
zwarn("Can't add module parameter `%s': %s",
nam, pm->level ?
"local parameter exists" :
diff --git a/Src/openssh_bsd_setres_id.c b/Src/openssh_bsd_setres_id.c
index 65e91a40c..217a6d074 100644
--- a/Src/openssh_bsd_setres_id.c
+++ b/Src/openssh_bsd_setres_id.c
@@ -46,6 +46,8 @@
*
*/
+#include "zsh.mdh"
+#include "openssh_bsd_setres_id.pro"
#include <sys/types.h>
@@ -53,8 +55,6 @@
#include <unistd.h>
#include <string.h>
-#include "zsh.mdh"
-
#if defined(ZSH_IMPLEMENT_SETRESGID) || defined(BROKEN_SETRESGID)
int
setresgid(gid_t rgid, gid_t egid, gid_t sgid)
diff --git a/Src/options.c b/Src/options.c
index 08ba71917..a1fe918fc 100644
--- a/Src/options.c
+++ b/Src/options.c
@@ -105,6 +105,7 @@ static struct optname optns[] = {
{{NULL, "bsdecho", OPT_EMULATE|OPT_SH}, BSDECHO},
{{NULL, "caseglob", OPT_ALL}, CASEGLOB},
{{NULL, "casematch", OPT_ALL}, CASEMATCH},
+{{NULL, "casepaths", 0}, CASEPATHS},
{{NULL, "cbases", 0}, CBASES},
{{NULL, "cprecedences", OPT_EMULATE|OPT_NONZSH}, CPRECEDENCES},
{{NULL, "cdablevars", OPT_EMULATE}, CDABLEVARS},
@@ -114,6 +115,7 @@ static struct optname optns[] = {
{{NULL, "checkjobs", OPT_EMULATE|OPT_ZSH}, CHECKJOBS},
{{NULL, "checkrunningjobs", OPT_EMULATE|OPT_ZSH}, CHECKRUNNINGJOBS},
{{NULL, "clobber", OPT_EMULATE|OPT_ALL}, CLOBBER},
+{{NULL, "clobberempty", 0}, CLOBBEREMPTY},
{{NULL, "combiningchars", 0}, COMBININGCHARS},
{{NULL, "completealiases", 0}, COMPLETEALIASES},
{{NULL, "completeinword", 0}, COMPLETEINWORD},
@@ -248,6 +250,7 @@ static struct optname optns[] = {
{{NULL, "shnullcmd", OPT_EMULATE|OPT_BOURNE}, SHNULLCMD},
{{NULL, "shoptionletters", OPT_EMULATE|OPT_BOURNE}, SHOPTIONLETTERS},
{{NULL, "shortloops", OPT_EMULATE|OPT_NONBOURNE},SHORTLOOPS},
+{{NULL, "shortrepeat", OPT_EMULATE}, SHORTREPEAT},
{{NULL, "shwordsplit", OPT_EMULATE|OPT_BOURNE}, SHWORDSPLIT},
{{NULL, "singlecommand", OPT_SPECIAL}, SINGLECOMMAND},
{{NULL, "singlelinezle", OPT_KSH}, SINGLELINEZLE},
@@ -256,6 +259,7 @@ static struct optname optns[] = {
{{NULL, "transientrprompt", 0}, TRANSIENTRPROMPT},
{{NULL, "trapsasync", 0}, TRAPSASYNC},
{{NULL, "typesetsilent", OPT_EMULATE|OPT_BOURNE}, TYPESETSILENT},
+{{NULL, "typesettounset", OPT_EMULATE|OPT_BOURNE}, TYPESETTOUNSET},
{{NULL, "unset", OPT_EMULATE|OPT_BSHELL}, UNSET},
{{NULL, "verbose", 0}, VERBOSE},
{{NULL, "vi", 0}, VIMODE},
@@ -807,7 +811,7 @@ dosetopt(int optno, int value, int force, char *new_opts)
return -1;
}
-# ifdef HAVE_INITGROUPS
+# ifdef USE_INITGROUPS
/* Set the supplementary groups list.
*
* Note that on macOS, FreeBSD, and possibly some other platforms,
diff --git a/Src/params.c b/Src/params.c
index 863b32600..970a207e4 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -63,7 +63,6 @@ char **pparams, /* $argv */
**mailpath, /* $mailpath */
**manpath, /* $manpath */
**psvar, /* $psvar */
- **watch, /* $watch */
**zsh_eval_context; /* $zsh_eval_context */
/**/
mod_export
@@ -98,8 +97,10 @@ char *ifs, /* $IFS */
*pwd; /* $PWD */
/**/
-mod_export
-zlong lastval, /* $? */
+mod_export volatile zlong
+ lastval; /* $? */
+/**/
+mod_export zlong
mypid, /* $$ */
lastpid, /* $! */
zterm_columns, /* $COLUMNS */
@@ -192,6 +193,10 @@ mod_export const struct gsu_hash stdhash_gsu =
mod_export const struct gsu_hash nullsethash_gsu =
{ hashgetfn, nullsethashfn, nullunsetfn };
+/**/
+mod_export const struct gsu_scalar colonarr_gsu =
+{ colonarrgetfn, colonarrsetfn, stdunsetfn };
+
/* Non standard methods (not exported) */
static const struct gsu_integer pound_gsu =
@@ -257,9 +262,6 @@ static const struct gsu_integer varint_readonly_gsu =
static const struct gsu_integer zlevar_gsu =
{ intvargetfn, zlevarsetfn, stdunsetfn };
-static const struct gsu_scalar colonarr_gsu =
-{ colonarrgetfn, colonarrsetfn, stdunsetfn };
-
static const struct gsu_integer argc_gsu =
{ poundgetfn, nullintsetfn, stdunsetfn };
static const struct gsu_array pipestatus_gsu =
@@ -396,7 +398,6 @@ IPDEF8("CDPATH", &cdpath, "cdpath", PM_TIED),
IPDEF8("FIGNORE", &fignore, "fignore", PM_TIED),
IPDEF8("FPATH", &fpath, "fpath", PM_TIED),
IPDEF8("MAILPATH", &mailpath, "mailpath", PM_TIED),
-IPDEF8("WATCH", &watch, "watch", PM_TIED),
IPDEF8("PATH", &path, "path", PM_RESTRICTED|PM_TIED),
IPDEF8("PSVAR", &psvar, "psvar", PM_TIED),
IPDEF8("ZSH_EVAL_CONTEXT", &zsh_eval_context, "zsh_eval_context", PM_READONLY_SPECIAL|PM_TIED),
@@ -428,7 +429,6 @@ IPDEF9("fpath", &fpath, "FPATH", PM_TIED),
IPDEF9("mailpath", &mailpath, "MAILPATH", PM_TIED),
IPDEF9("manpath", &manpath, "MANPATH", PM_TIED),
IPDEF9("psvar", &psvar, "PSVAR", PM_TIED),
-IPDEF9("watch", &watch, "WATCH", PM_TIED),
IPDEF9("zsh_eval_context", &zsh_eval_context, "ZSH_EVAL_CONTEXT", PM_TIED|PM_READONLY_SPECIAL),
@@ -451,7 +451,6 @@ IPDEF8("CDPATH", &cdpath, NULL, 0),
IPDEF8("FIGNORE", &fignore, NULL, 0),
IPDEF8("FPATH", &fpath, NULL, 0),
IPDEF8("MAILPATH", &mailpath, NULL, 0),
-IPDEF8("WATCH", &watch, NULL, 0),
IPDEF8("PATH", &path, NULL, PM_RESTRICTED),
IPDEF8("PSVAR", &psvar, NULL, 0),
IPDEF8("ZSH_EVAL_CONTEXT", &zsh_eval_context, NULL, PM_READONLY_SPECIAL),
@@ -759,7 +758,7 @@ split_env_string(char *env, char **name, char **value)
*/
static int dontimport(int flags)
{
- /* If explicitly marked as don't export */
+ /* If explicitly marked as don't import */
if (flags & PM_DONTIMPORT)
return 1;
/* If value already exported */
@@ -819,7 +818,6 @@ createparamtable(void)
* given them in the environment. */
opts[ALLEXPORT] = 0;
setiparam("MAILCHECK", 60);
- setiparam("LOGCHECK", 60);
setiparam("KEYTIMEOUT", 40);
setiparam("LISTMAX", 100);
/*
@@ -834,16 +832,18 @@ createparamtable(void)
*/
setsparam("TMPPREFIX", ztrdup_metafy(DEFAULT_TMPPREFIX));
setsparam("TIMEFMT", ztrdup_metafy(DEFAULT_TIMEFMT));
- setsparam("WATCHFMT", ztrdup_metafy(default_watchfmt));
hostnam = (char *)zalloc(256);
gethostname(hostnam, 256);
setsparam("HOST", ztrdup_metafy(hostnam));
zfree(hostnam, 256);
- setsparam("LOGNAME",
- ztrdup_metafy((str = getlogin()) && *str ?
- str : cached_username));
+ setsparam("LOGNAME", ztrdup_metafy(
+#ifndef DISABLE_DYNAMIC_NSS
+ (str = getlogin()) && *str ? str :
+#endif
+ cached_username
+ ));
#if !defined(HAVE_PUTENV) && !defined(USE_SET_UNSET_ENV)
/* Copy the environment variables we are inheriting to dynamic *
@@ -1008,6 +1008,11 @@ createparam(char *name, int flags)
(oldpm->node.flags & PM_SPECIAL) ||
/* 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",
+ name);
+ return NULL;
+ }
oldpm->node.flags &= ~PM_UNSET;
if ((oldpm->node.flags & PM_SPECIAL) && oldpm->ename) {
Param altpm =
@@ -2093,7 +2098,8 @@ fetchvalue(Value v, char **pptr, int bracks, int flags)
if (sav)
*s = sav;
*pptr = s;
- if (!pm || (pm->node.flags & PM_UNSET))
+ if (!pm || ((pm->node.flags & PM_UNSET) &&
+ !(pm->node.flags & PM_DECLARED)))
return NULL;
if (v)
memset(v, 0, sizeof(*v));
@@ -3055,6 +3061,7 @@ assignsparam(char *s, char *val, int flags)
* Don't warn about anything.
*/
flags &= ~ASSPM_WARN;
+ v->pm->node.flags &= ~PM_DEFAULTED;
}
*ss = '[';
v = NULL;
@@ -3080,6 +3087,7 @@ assignsparam(char *s, char *val, int flags)
}
if (flags & ASSPM_WARN)
check_warn_pm(v->pm, "scalar", created, 1);
+ v->pm->node.flags &= ~PM_DEFAULTED;
if (flags & ASSPM_AUGMENT) {
if (v->start == 0 && v->end == -1) {
switch (PM_TYPE(v->pm->node.flags)) {
@@ -3232,6 +3240,7 @@ assignaparam(char *s, char **val, int flags)
if (flags & ASSPM_WARN)
check_warn_pm(v->pm, "array", created, may_warn_about_nested_vars);
+ v->pm->node.flags &= ~PM_DEFAULTED;
/*
* At this point, we may have array entries consisting of
@@ -3444,6 +3453,7 @@ sethparam(char *s, char **val)
return NULL;
}
check_warn_pm(v->pm, "associative array", checkcreate, 1);
+ v->pm->node.flags &= ~PM_DEFAULTED;
setarrvalue(v, val);
unqueue_signals();
return v->pm;
@@ -3515,6 +3525,7 @@ assignnparam(char *s, mnumber val, int flags)
if (flags & ASSPM_WARN)
check_warn_pm(v->pm, "numeric", 0, 1);
}
+ v->pm->node.flags &= ~PM_DEFAULTED;
setnumvalue(v, val);
unqueue_signals();
return v->pm;
@@ -3619,6 +3630,7 @@ unsetparam_pm(Param pm, int altflag, int exp)
else
altremove = NULL;
+ pm->node.flags &= ~PM_DECLARED; /* like ksh, not like bash */
if (!(pm->node.flags & PM_UNSET))
pm->gsu.s->unsetfn(pm, exp);
if (pm->env)
@@ -3652,6 +3664,8 @@ unsetparam_pm(Param pm, int altflag, int exp)
}
zsfree(altremove);
+ if (!(pm->node.flags & PM_SPECIAL))
+ pm->gsu.s = &stdscalar_gsu;
}
/*
@@ -4074,7 +4088,7 @@ arrvarsetfn(Param pm, char **x)
}
/**/
-char *
+mod_export char *
colonarrgetfn(Param pm)
{
char ***dptr = (char ***)pm->u.data;
@@ -4082,7 +4096,7 @@ colonarrgetfn(Param pm)
}
/**/
-void
+mod_export void
colonarrsetfn(Param pm, char *x)
{
char ***dptr = (char ***)pm->u.data;
@@ -4116,6 +4130,11 @@ tiedarrsetfn(Param pm, char *x)
if (*dptr->arrptr)
freearray(*dptr->arrptr);
+ else if (pm->ename) {
+ Param altpm = (Param) paramtab->getnode(paramtab, pm->ename);
+ if (altpm)
+ altpm->node.flags &= ~PM_DEFAULTED;
+ }
if (x) {
char sepbuf[3];
if (imeta(dptr->joinchar))
@@ -4414,7 +4433,7 @@ usernamegetfn(UNUSED(Param pm))
void
usernamesetfn(UNUSED(Param pm), char *x)
{
-#if defined(HAVE_SETUID) && defined(HAVE_GETPWNAM)
+#if defined(HAVE_SETUID) && defined(USE_GETPWNAM)
struct passwd *pswd;
if (x && (pswd = getpwnam(x)) && (pswd->pw_uid != cached_uid)) {
@@ -4431,7 +4450,7 @@ usernamesetfn(UNUSED(Param pm), char *x)
cached_uid = pswd->pw_uid;
}
}
-#endif /* HAVE_SETUID && HAVE_GETPWNAM */
+#endif /* HAVE_SETUID && USE_GETPWNAM */
zsfree(x);
}
@@ -5035,6 +5054,7 @@ arrfixenv(char *s, char **t)
if (isset(ALLEXPORT))
pm->node.flags |= PM_EXPORTED;
+ pm->node.flags &= ~PM_DEFAULTED;
/*
* Do not "fix" parameters that were not exported
@@ -5569,6 +5589,14 @@ startparamscope(void)
locallevel++;
}
+#ifdef USE_LOCALE
+/*
+ * Flag that one of the special LC_ functions or LANG changed on scope
+ * end
+ */
+static int lc_update_needed;
+#endif /* USE_LOCALE */
+
/* End a parameter scope: delete the parameters local to the scope. */
/**/
@@ -5579,7 +5607,28 @@ endparamscope(void)
locallevel--;
/* This pops anything from a higher locallevel */
saveandpophiststack(0, HFILE_USE_OPTIONS);
+#ifdef USE_LOCALE
+ lc_update_needed = 0;
+#endif
scanhashtable(paramtab, 0, 0, 0, scanendscope, 0);
+#ifdef USE_LOCALE
+ if (lc_update_needed)
+ {
+ /* Locale changed --- ensure it is restored. */
+ char *val;
+ if ((val = getsparam_u("LC_ALL")) && *val) {
+ setlocale(LC_ALL, val);
+ } else {
+ struct localename *ln;
+ if ((val = getsparam_u("LANG")) && *val)
+ setlang(val);
+ for (ln = lc_names; ln->name; ln++) {
+ if ((val = getsparam_u(ln->name)) && *val)
+ setlocale(ln->category, val);
+ }
+ }
+ }
+#endif /* USE_LOCALE */
unqueue_signals();
}
@@ -5600,6 +5649,11 @@ scanendscope(HashNode hn, UNUSED(int flags))
*/
Param tpm = pm->old;
+#ifdef USE_LOCALE
+ if (!strncmp(pm->node.nam, "LC_", 3) ||
+ !strcmp(pm->node.nam, "LANG"))
+ lc_update_needed = 1;
+#endif
if (!strcmp(pm->node.nam, "SECONDS"))
{
setsecondstype(pm, PM_TYPE(tpm->node.flags), PM_TYPE(pm->node.flags));
@@ -5805,8 +5859,9 @@ printparamnode(HashNode hn, int printflags)
Param peer = NULL;
if (p->node.flags & PM_UNSET) {
- if (printflags & (PRINT_POSIX_READONLY|PRINT_POSIX_EXPORT) &&
- p->node.flags & (PM_READONLY|PM_EXPORTED)) {
+ if ((printflags & (PRINT_POSIX_READONLY|PRINT_POSIX_EXPORT) &&
+ p->node.flags & (PM_READONLY|PM_EXPORTED)) ||
+ (p->node.flags & PM_DEFAULTED) == PM_DEFAULTED) {
/*
* Special POSIX rules: show the parameter as readonly/exported
* even though it's unset, but with no value.
@@ -5833,8 +5888,12 @@ printparamnode(HashNode hn, int printflags)
* don't.
*/
if (printflags & PRINT_POSIX_EXPORT) {
+ if (!(p->node.flags & PM_EXPORTED))
+ return;
printf("export ");
} else if (printflags & PRINT_POSIX_READONLY) {
+ if (!(p->node.flags & PM_READONLY))
+ return;
printf("readonly ");
} else if (locallevel && p->level >= locallevel) {
printf("typeset "); /* printf("local "); */
diff --git a/Src/parse.c b/Src/parse.c
index de1b27967..d612b7e17 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -102,6 +102,13 @@ struct heredocs *hdocs;
* The parser now produces word code, reducing memory consumption compared
* to the nested structs we had before.
*
+ * Word codes are represented by the "wordcode" type.
+ *
+ * Each wordcode variable consists of a "code", in the least-significant bits
+ * of the value, and "data" in the other bits. The macros wc_code() and wc_data()
+ * access the "code" and "data" parts of a wordcode. The macros wc_bdata() and
+ * wc_bld() build wordcodes from code and data.
+ *
* Word code layout:
*
* WC_END
@@ -166,8 +173,13 @@ struct heredocs *hdocs;
* - followed by offset to first string
* - followed by length of string table
* - followed by number of patterns for body
+ * - followed by an integer indicating tracing status
* - followed by codes for body
* - followed by strings for body
+ * - if number of names is 0, followed by:
+ * - the offset to the end of the funcdef
+ * - the number of arguments to the function
+ * - the arguments to the function
*
* WC_FOR
* - data contains type (list, ...) and offset to after body
@@ -253,14 +265,24 @@ struct heredocs *hdocs;
* to avoid a lot of string parsing and some more string duplication.
*/
-/**/
-int eclen, ecused, ecnpats;
-/**/
-Wordcode ecbuf;
-/**/
-Eccstr ecstrs;
-/**/
-int ecsoffs, ecssub, ecnfunc;
+/* Number of wordcodes allocated. */
+static int eclen;
+/* Number of wordcodes populated. */
+static int ecused;
+/* Number of patterns... */
+static int ecnpats;
+
+static Wordcode ecbuf;
+
+static Eccstr ecstrs;
+
+static int ecsoffs, ecssub;
+
+/*
+ * ### The number of starts and ends of function definitions up to this point.
+ * Never decremented.
+ */
+static int ecnfunc;
#define EC_INIT_SIZE 256
#define EC_DOUBLE_THRESHOLD 32768
@@ -364,7 +386,11 @@ ecispace(int p, int n)
ecadjusthere(p, n);
}
-/* Add one wordcode. */
+/*
+ * Add one wordcode.
+ *
+ * Return the index of the added wordcode.
+ */
static int
ecadd(wordcode c)
@@ -403,6 +429,7 @@ ecstrcode(char *s)
unsigned val = hasher(s);
if ((l = strlen(s) + 1) && l <= 4) {
+ /* Short string. */
t = has_token(s);
wordcode c = (t ? 3 : 2);
switch (l) {
@@ -413,11 +440,13 @@ ecstrcode(char *s)
}
return c;
} else {
+ /* Long string. */
Eccstr p, *pp;
long cmp;
for (pp = &ecstrs; (p = *pp); ) {
if (!(cmp = p->nfunc - ecnfunc) && !(cmp = (((long)p->hashval) - ((long)val))) && !(cmp = strcmp(p->str, s))) {
+ /* Re-use the existing string. */
return p->offs;
}
pp = (cmp < 0 ? &(p->left) : &(p->right));
@@ -494,7 +523,12 @@ init_parse(void)
/* Build eprog. */
-/* careful: copy_ecstr is from arg1 to arg2, unlike memcpy */
+/*
+ * Copy the strings of s and all its descendants in the binary tree to the
+ * memory block p.
+ *
+ * careful: copy_ecstr is from arg1 to arg2, unlike memcpy
+ */
static void
copy_ecstr(Eccstr s, char *p)
@@ -1559,7 +1593,7 @@ par_repeat(int *cmplx)
if (tok != ZEND)
YYERRORV(oecused);
zshlex();
- } else if (unset(SHORTLOOPS)) {
+ } else if (unset(SHORTLOOPS) && unset(SHORTREPEAT)) {
YYERRORV(oecused);
} else
par_save_list1(cmplx);
@@ -1636,6 +1670,7 @@ par_funcdef(int *cmplx)
int oecused = ecused, num = 0, onp, p, c = 0;
int so, oecssub = ecssub;
zlong oldlineno = lineno;
+ int do_tracing = 0;
lineno = 0;
nocorrect = 1;
@@ -1643,7 +1678,21 @@ par_funcdef(int *cmplx)
zshlex();
p = ecadd(0);
- ecadd(0);
+ ecadd(0); /* p + 1 */
+
+ /* Consume an initial (-T), (--), or (-T --).
+ * Anything else is a literal function name.
+ */
+ if (tok == STRING && tokstr[0] == Dash) {
+ if (tokstr[1] == 'T' && !tokstr[2]) {
+ ++do_tracing;
+ zshlex();
+ }
+ if (tok == STRING && tokstr[0] == Dash &&
+ tokstr[1] == Dash && !tokstr[2]) {
+ zshlex();
+ }
+ }
while (tok == STRING) {
if ((*tokstr == Inbrace || *tokstr == '{') &&
@@ -1655,9 +1704,10 @@ par_funcdef(int *cmplx)
num++;
zshlex();
}
- ecadd(0);
- ecadd(0);
- ecadd(0);
+ ecadd(0); /* p + num + 2 */
+ ecadd(0); /* p + num + 3 */
+ ecadd(0); /* p + num + 4 */
+ ecadd(0); /* p + num + 5 */
nocorrect = 0;
incmdpos = 1;
@@ -1695,18 +1745,20 @@ par_funcdef(int *cmplx)
ecadd(WCB_END());
ecbuf[p + num + 2] = so - oecssub;
- ecbuf[p + num + 3] = ecsoffs - so;
- ecbuf[p + num + 4] = ecnpats;
- ecbuf[p + 1] = num;
+ ecbuf[p + num + 3] = ecsoffs - so; /* "length of string table" */
+ ecbuf[p + num + 4] = ecnpats; /* "number of patterns for body" */
+ ecbuf[p + num + 5] = do_tracing;
+ ecbuf[p + 1] = num; /* "number of names" */
ecnpats = onp;
ecssub = oecssub;
ecnfunc++;
- ecbuf[p] = WCB_FUNCDEF(ecused - 1 - p);
+ ecbuf[p] = WCB_FUNCDEF(ecused - 1 - p); /* "offset to after body" */
+ /* If it's an anonymous function... */
if (num == 0) {
- /* Unnamed function */
+ /* ... look for arguments to it. */
int parg = ecadd(0);
ecadd(0);
while (tok == STRING) {
@@ -2000,7 +2052,7 @@ par_simple(int *cmplx, int nr)
/* Error if preceding assignments */
if (assignments || postassigns)
YYERROR(oecused);
- if (hasalias && !isset(ALIASFUNCDEF) && argc &&
+ if (isset(EXECOPT) && hasalias && !isset(ALIASFUNCDEF) && argc &&
hasalias != input_hasalias()) {
zwarn("defining function based on alias `%s'", hasalias);
YYERROR(oecused);
@@ -2019,6 +2071,7 @@ par_simple(int *cmplx, int nr)
ecadd(0);
ecadd(0);
ecadd(0);
+ ecadd(0);
ecnfunc++;
ecssub = so = ecsoffs;
@@ -2074,6 +2127,7 @@ par_simple(int *cmplx, int nr)
ecbuf[p + argc + 2] = so - oecssub;
ecbuf[p + argc + 3] = ecsoffs - so;
ecbuf[p + argc + 4] = ecnpats;
+ ecbuf[p + argc + 5] = 0;
ecnpats = onp;
ecssub = oecssub;
@@ -2081,8 +2135,9 @@ par_simple(int *cmplx, int nr)
ecbuf[p] = WCB_FUNCDEF(ecused - 1 - p);
+ /* If it's an anonymous function... */
if (argc == 0) {
- /* Unnamed function */
+ /* ... look for arguments to it. */
int parg = ecadd(0);
ecadd(0);
while (tok == STRING || IS_REDIROP(tok)) {
@@ -2454,7 +2509,7 @@ par_cond_2(void)
* In "test" compatibility mode, "! -a ..." and "! -o ..."
* are treated as "[string] [and] ..." and "[string] [or] ...".
*/
- if (!(n_testargs > 1 && (check_cond(*testargs, "a") ||
+ if (!(n_testargs > 2 && (check_cond(*testargs, "a") ||
check_cond(*testargs, "o"))))
{
condlex();
@@ -2756,10 +2811,6 @@ freeeprog(Eprog p)
DPUTS(p->nref > 0 && (p->flags & EF_HEAP), "Heap EPROG has nref > 0");
DPUTS(p->nref < 0 && !(p->flags & EF_HEAP), "Real EPROG has nref < 0");
DPUTS(p->nref < -1, "Uninitialised EPROG nref");
-#ifdef MAX_FUNCTION_DEPTH
- DPUTS(zsh_funcnest >=0 && p->nref > zsh_funcnest + 10,
- "Overlarge EPROG nref");
-#endif
if (p->nref > 0 && !--p->nref) {
for (i = p->npats, pp = p->pats; i--; pp++)
freepatprog(*pp);
@@ -2773,6 +2824,13 @@ freeeprog(Eprog p)
}
}
+/*
+ * dup is of type 'enum ec_dup_t'.
+ *
+ * If tokflag is not NULL, *tokflag will be set to 1 if the string contains
+ * tokens and to 0 otherwise.
+ */
+
/**/
char *
ecgetstr(Estate s, int dup, int *tokflag)
diff --git a/Src/patchlevel.h.release b/Src/patchlevel.h.release
index e4475aae7..05403a9e4 100644
--- a/Src/patchlevel.h.release
+++ b/Src/patchlevel.h.release
@@ -1 +1 @@
-#define ZSH_PATCHLEVEL "zsh-5.8.1-0-g1a490c7"
+#define ZSH_PATCHLEVEL "zsh-5.8.1.2-test-0-g6e55c92"
diff --git a/Src/pattern.c b/Src/pattern.c
index c7c2c8bea..c0e31b78e 100644
--- a/Src/pattern.c
+++ b/Src/pattern.c
@@ -509,7 +509,7 @@ void
patcompstart(void)
{
patcompcharsset();
- if (isset(CASEGLOB))
+ if (isset(CASEGLOB) || isset(CASEPATHS))
patglobflags = 0;
else
patglobflags = GF_IGNCASE;
@@ -632,6 +632,13 @@ patcompile(char *exp, int inflags, char **endexp)
p->patmlen = len;
p->patnpar = patnpar-1;
+#ifndef __CYGWIN__ /* The filesystem itself is case-insensitive on Cygwin */
+ if ((patflags & PAT_FILE) && !isset(CASEGLOB) && !(patflags & PAT_PURES)) {
+ p->globflags |= GF_IGNCASE;
+ p->globend |= GF_IGNCASE;
+ }
+#endif
+
if (!strp) {
pscan = (Upat)(patout + startoff);
@@ -1250,7 +1257,7 @@ patcomppiece(int *flagp, int paren)
int hash, count;
union upat up;
char *nptr, *str0, *ptr, *patprev;
- zrange_t from, to;
+ zrange_t from = 0, to;
char *charstart;
flags = 0;
diff --git a/Src/prompt.c b/Src/prompt.c
index 91e21c8e9..738c7fc7a 100644
--- a/Src/prompt.c
+++ b/Src/prompt.c
@@ -268,7 +268,7 @@ parsecolorchar(zattr arg, int is_fg)
bv->fm--;
}
} else
- arg = match_colour(NULL, 1, arg);
+ arg = match_colour(NULL, is_fg, arg);
return arg;
}
@@ -1055,9 +1055,9 @@ tsetcap(int cap, int flags)
if (txtisset(TXTUNDERLINE))
tsetcap(TCUNDERLINEBEG, flags);
if (txtisset(TXTFGCOLOUR))
- set_colour_attribute(txtattrmask, COL_SEQ_FG, TSC_PROMPT);
+ set_colour_attribute(txtattrmask, COL_SEQ_FG, flags);
if (txtisset(TXTBGCOLOUR))
- set_colour_attribute(txtattrmask, COL_SEQ_BG, TSC_PROMPT);
+ set_colour_attribute(txtattrmask, COL_SEQ_BG, flags);
}
}
}
@@ -1706,38 +1706,22 @@ match_colour(const char **teststrp, int is_fg, int colour)
return TXT_ERROR;
}
}
- /*
- * Try termcap for numbered characters if possible.
- * Don't for named characters, since our best bet
- * of getting the names right is with ANSI sequences.
- */
- if (!named && tccan(tc)) {
- if (tccolours >= 0 && colour >= tccolours) {
- /*
- * Out of range of termcap colours.
- * Can we assume ANSI colours work?
- */
- if (colour > 7)
- return TXT_ERROR; /* No. */
- } else {
- /*
- * We can handle termcap colours and the number
- * is in range, so use termcap.
- */
- on |= is_fg ? TXT_ATTR_FG_TERMCAP :
- TXT_ATTR_BG_TERMCAP;
- }
- }
+
+ /* Out of range of termcap colours and basic ANSI set. */
+ if (tccan(tc) && colour > 7 && colour >= tccolours)
+ return TXT_ERROR;
+
return on | (zattr)colour << shft;
}
/*
* Match a set of highlights in the given teststr.
* Set *on_var to reflect the values found.
+ * Return a pointer to the first character not consumed.
*/
/**/
-mod_export void
+mod_export const char *
match_highlight(const char *teststr, zattr *on_var)
{
int found = 1;
@@ -1755,7 +1739,7 @@ match_highlight(const char *teststr, zattr *on_var)
atr = match_colour(&teststr, is_fg, 0);
if (*teststr == ',')
teststr++;
- else if (*teststr)
+ else if (*teststr && *teststr != ' ')
break;
found = 1;
/* skip out of range colours but keep scanning attributes */
@@ -1768,7 +1752,7 @@ match_highlight(const char *teststr, zattr *on_var)
if (*val == ',')
val++;
- else if (*val)
+ else if (*val && *val != ' ')
break;
*on_var |= hl->mask_on;
@@ -1779,6 +1763,8 @@ match_highlight(const char *teststr, zattr *on_var)
}
}
}
+
+ return teststr;
}
/*
@@ -1788,7 +1774,7 @@ match_highlight(const char *teststr, zattr *on_var)
*/
static int
-output_colour(int colour, int fg_bg, int use_tc, int truecol, char *buf)
+output_colour(int colour, int fg_bg, int truecol, char *buf)
{
int atrlen = 3, len;
char *ptr = buf;
@@ -1806,7 +1792,7 @@ output_colour(int colour, int fg_bg, int use_tc, int truecol, char *buf)
* used instead of termcap even for colour > 7. Here this just emits the
* color number, so it works fine for both zle_highlight and tercap cases
*/
- } else if (use_tc || colour > 7) {
+ } else if (colour > 7) {
char digbuf[DIGBUFSIZE];
sprintf(digbuf, "%d", colour);
len = strlen(digbuf);
@@ -1843,7 +1829,6 @@ output_highlight(zattr atr, char *buf)
if (atr & TXTFGCOLOUR) {
len = output_colour(txtchangeget(atr, TXT_ATTR_FG_COL),
COL_SEQ_FG,
- (atr & TXT_ATTR_FG_TERMCAP),
(atr & TXT_ATTR_FG_24BIT),
ptr);
atrlen += len;
@@ -1860,7 +1845,6 @@ output_highlight(zattr atr, char *buf)
}
len = output_colour(txtchangeget(atr, TXT_ATTR_BG_COL),
COL_SEQ_BG,
- (atr & TXT_ATTR_BG_TERMCAP),
(atr & TXT_ATTR_BG_24BIT),
ptr);
atrlen += len;
@@ -2025,7 +2009,6 @@ free_colour_buffer(void)
* fg_bg indicates if we're changing the foreground or background.
* tc indicates the termcap code to use, if appropriate.
* def indicates if we're resetting the default colour.
- * use_termcap indicates if we should use termcap to output colours.
* flags is either 0 or TSC_PROMPT.
*/
@@ -2035,7 +2018,7 @@ set_colour_attribute(zattr atr, int fg_bg, int flags)
{
char *ptr;
int do_free, is_prompt = (flags & TSC_PROMPT) ? 1 : 0;
- int colour, tc, def, use_termcap, use_truecolor;
+ int colour, tc, def, use_truecolor;
int is_default_zle_highlight = 1;
if (fg_bg == COL_SEQ_FG) {
@@ -2043,13 +2026,11 @@ set_colour_attribute(zattr atr, int fg_bg, int flags)
tc = TCFGCOLOUR;
def = txtchangeisset(atr, TXTNOFGCOLOUR);
use_truecolor = txtchangeisset(atr, TXT_ATTR_FG_24BIT);
- use_termcap = txtchangeisset(atr, TXT_ATTR_FG_TERMCAP);
} else {
colour = txtchangeget(atr, TXT_ATTR_BG_COL);
tc = TCBGCOLOUR;
def = txtchangeisset(atr, TXTNOBGCOLOUR);
use_truecolor = txtchangeisset(atr, TXT_ATTR_BG_24BIT);
- use_termcap = txtchangeisset(atr, TXT_ATTR_BG_TERMCAP);
}
/* Test if current zle_highlight settings are customized, or
@@ -2064,17 +2045,14 @@ set_colour_attribute(zattr atr, int fg_bg, int flags)
}
/*
- * If we're not restoring the default, and either have a
- * colour value that is too large for ANSI, or have been told
- * to use the termcap sequence, try to use the termcap sequence.
- * True color is not covered by termcap.
+ * If we're not restoring the default or applying true color,
+ * try to use the termcap sequence.
*
* We have already sanitised the values we allow from the
* highlighting variables, so much of this shouldn't be
* necessary at this point, but we might as well be safe.
*/
- if (!def && !use_truecolor &&
- (is_default_zle_highlight && (colour > 7 || use_termcap)))
+ if (!def && !use_truecolor && is_default_zle_highlight)
{
/*
* We can if it's available, and either we couldn't get
@@ -2094,7 +2072,8 @@ set_colour_attribute(zattr atr, int fg_bg, int flags)
*bv->bp++ = Outpar;
}
} else {
- tputs(tgoto(tcstr[tc], colour, colour), 1, putshout);
+ tputs(tgoto(tcstr[tc], colour, colour), 1,
+ (flags & TSC_RAW) ? putraw : putshout);
}
/* That worked. */
return;
@@ -2153,7 +2132,7 @@ set_colour_attribute(zattr atr, int fg_bg, int flags)
*bv->bp++ = Outpar;
}
} else
- tputs(colseq_buf, 1, putshout);
+ tputs(colseq_buf, 1, (flags & TSC_RAW) ? putraw : putshout);
if (do_free)
free_colour_buffer();
diff --git a/Src/signals.c b/Src/signals.c
index 96ff9e9b3..5c787e2a8 100644
--- a/Src/signals.c
+++ b/Src/signals.c
@@ -53,7 +53,7 @@ mod_export Eprog siglists[VSIGCOUNT];
/* Total count of trapped signals */
/**/
-mod_export int nsigtrapped;
+mod_export volatile int nsigtrapped;
/* Running an exit trap? */
@@ -72,20 +72,20 @@ static int exit_trap_posix;
/* Variables used by signal queueing */
/**/
-mod_export int queueing_enabled, queue_front, queue_rear;
+mod_export volatile int queueing_enabled, queue_front, queue_rear;
/**/
mod_export int signal_queue[MAX_QUEUE_SIZE];
/**/
mod_export sigset_t signal_mask_queue[MAX_QUEUE_SIZE];
#ifdef DEBUG
/**/
-mod_export int queue_in;
+mod_export volatile int queue_in;
#endif
/* Variables used by trap queueing */
/**/
-mod_export int trap_queueing_enabled, trap_queue_front, trap_queue_rear;
+mod_export volatile int trap_queueing_enabled, trap_queue_front, trap_queue_rear;
/**/
mod_export int trap_queue[MAX_QUEUE_SIZE];
@@ -539,7 +539,8 @@ wait_for_processes(void)
#endif
if (WIFEXITED(status) &&
pn->pid == jn->gleader &&
- killpg(pn->pid, 0) == -1) {
+ killpg(pn->pid, 0) == -1 &&
+ errno == ESRCH) {
if (last_attached_pgrp == jn->gleader &&
!(jn->stat & STAT_NOSTTY)) {
/*
@@ -671,9 +672,9 @@ zhandler(int sig)
if ((isset(PRIVILEGED) || isset(RESTRICTED)) &&
isset(INTERACTIVE) && (noerrexit & NOERREXIT_SIGNAL))
zexit(SIGINT, ZEXIT_SIGNAL);
+ errflag |= ERRFLAG_INT;
if (list_pipe || chline || simple_pline) {
breaks = loops;
- errflag |= ERRFLAG_INT;
inerrflush();
check_cursh_sig(SIGINT);
}
@@ -809,6 +810,7 @@ killjb(Job jn, int sig)
err = killpg(jn->gleader, sig);
if (sig == SIGCONT && err != -1)
makerunning(jn);
+ return err;
}
}
for (pn = jn->procs; pn; pn = pn->next) {
@@ -1265,19 +1267,19 @@ unqueue_traps(void)
/* Are we already executing a trap? */
/**/
-int intrap;
+volatile int intrap;
/* Is the current trap a function? */
/**/
-int trapisfunc;
+volatile int trapisfunc;
/*
* If the current trap is not a function, at what function depth
* did the trap get called?
*/
/**/
-int traplocallevel;
+volatile int traplocallevel;
/*
* sig is the signal number.
diff --git a/Src/sort.c b/Src/sort.c
index 8faf9349c..26949ad9c 100644
--- a/Src/sort.c
+++ b/Src/sort.c
@@ -135,12 +135,23 @@ eltpcmp(const void *a, const void *b)
#endif
if (sortnumeric) {
+ int mul = 0;
for (; *as == *bs && *as; as++, bs++);
#ifndef HAVE_STRCOLL
cmp = (int)STOUC(*as) - (int)STOUC(*bs);
#endif
- if (idigit(*as) || idigit(*bs)) {
+ if (sortnumeric < 0) {
+ if (*as == '-' && idigit(as[1]) && idigit(*bs)) {
+ cmp = -1;
+ mul = 1;
+ } else if (*bs == '-' && idigit(bs[1]) && idigit(*as)) {
+ cmp = 1;
+ mul = 1;
+ }
+ }
+ if (!mul && (idigit(*as) || idigit(*bs))) {
for (; as > ao && idigit(as[-1]); as--, bs--);
+ mul = (sortnumeric < 0 && as > ao && as[-1] == '-') ? -1 : 1;
if (idigit(*as) && idigit(*bs)) {
while (*as == '0')
as++;
@@ -148,13 +159,13 @@ eltpcmp(const void *a, const void *b)
bs++;
for (; idigit(*as) && *as == *bs; as++, bs++);
if (idigit(*as) || idigit(*bs)) {
- cmp = (int)STOUC(*as) - (int)STOUC(*bs);
+ cmp = mul * ((int)STOUC(*as) - (int)STOUC(*bs));
while (idigit(*as) && idigit(*bs))
as++, bs++;
if (idigit(*as) && !idigit(*bs))
- return sortdir;
+ return mul * sortdir;
if (idigit(*bs) && !idigit(*as))
- return -sortdir;
+ return -mul * sortdir;
}
}
}
@@ -195,7 +206,8 @@ zstrcmp(const char *as, const char *bs, int sortflags)
sortdir = 1;
sortnobslash = (sortflags & SORTIT_IGNORING_BACKSLASHES) ? 1 : 0;
- sortnumeric = (sortflags & SORTIT_NUMERICALLY) ? 1 : 0;
+ sortnumeric = (sortflags & SORTIT_NUMERICALLY_SIGNED) ? -1 :
+ (sortflags & SORTIT_NUMERICALLY) ? 1 : 0;
ret = eltpcmp(&aeptr, &beptr);
@@ -389,7 +401,8 @@ strmetasort(char **array, int sortwhat, int *unmetalenp)
oldsortnumeric = sortnumeric;
sortdir = (sortwhat & SORTIT_BACKWARDS) ? -1 : 1;
- sortnumeric = (sortwhat & SORTIT_NUMERICALLY) ? 1 : 0;
+ sortnumeric = (sortwhat & SORTIT_NUMERICALLY_SIGNED) ? -1 :
+ (sortwhat & SORTIT_NUMERICALLY) ? 1 : 0;
qsort(sortptrarr, nsort, sizeof(SortElt), eltpcmp);
diff --git a/Src/string.c b/Src/string.c
index 9e14ef949..5f439926e 100644
--- a/Src/string.c
+++ b/Src/string.c
@@ -103,7 +103,10 @@ wcs_ztrdup(const wchar_t *s)
#endif /* MULTIBYTE_SUPPORT */
-/* concatenate s1, s2, and s3 in dynamically allocated buffer */
+/* Concatenate s1, s2, and s3 into dynamically allocated buffer.
+ *
+ * To concatenate four or more strings, see zjoin().
+ */
/**/
mod_export char *
diff --git a/Src/subst.c b/Src/subst.c
index 79efc9ad2..0f98e6ea3 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -765,7 +765,7 @@ filesubstr(char **namptr, int assign)
*namptr = dyncat(res, ptr2+1);
return 1;
}
- if (isset(NOMATCH))
+ if (isset(NOMATCH) && isset(EXECOPT))
zerr("no directory expansion: ~[%s]", tmp);
return 0;
} else if (!inblank(str[1]) && isend(*ptr) &&
@@ -796,7 +796,7 @@ filesubstr(char **namptr, int assign)
*namptr = dyncat(hom, ptr);
return 1;
}
- } else if (*str == Equals && isset(EQUALS) && str[1]) { /* =foo */
+ } else if (*str == Equals && isset(EQUALS) && str[1] && str[1] != Inpar) { /* =foo */
char *expn = equalsubstr(str+1, assign, isset(NOMATCH));
if (expn) {
*namptr = expn;
@@ -1565,6 +1565,11 @@ check_colon_subscript(char *str, char **endp)
if (!*str || ialpha(*str) || *str == '&')
return NULL;
+ if (*str == ':') {
+ *endp = str;
+ return dupstring("0");
+ }
+
*endp = parse_subscript(str, 0, ':');
if (!*endp) {
/* No trailing colon? */
@@ -1575,8 +1580,10 @@ check_colon_subscript(char *str, char **endp)
sav = **endp;
**endp = '\0';
str = dupstring(str);
- if (parsestr(&str))
+ if (parsestr(&str)) {
+ **endp = sav;
return NULL;
+ }
singsub(&str);
remnulargs(str);
untokenize(str);
@@ -1701,7 +1708,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
/*
* This expressive name refers to the set of flags which
* is applied to matching for #, %, / and their doubled variants:
- * (M), (R), (B), (E), (N), (S).
+ * (M), (R), (B), (E), (N), (S), (*).
*/
int flags = 0;
/* Value from (I) flag, used for ditto. */
@@ -1847,6 +1854,12 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
* nested (P) flags.
*/
int fetch_needed;
+ /*
+ * If an array parameter is quoted but has :offset:length (as in
+ * "${array:off:len}"), we apply :off:len as array index before
+ * joining the array into a string (for compatibility with ksh/bash).
+ */
+ int quoted_array_with_offset = 0;
*s++ = '\0';
/*
@@ -1923,6 +1936,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
case '@':
nojoin = 2; /* nojoin = 2 means force */
break;
+ case '*':
+ case Star:
+ flags |= SUB_EGLOB;
+ break;
case 'M':
flags |= SUB_MATCH;
break;
@@ -1972,6 +1989,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
case 'n':
sortit |= SORTIT_NUMERICALLY;
break;
+ case '-':
+ case Dash:
+ sortit |= SORTIT_NUMERICALLY_SIGNED;
+ break;
case 'a':
sortit |= SORTIT_SOMEHOW;
indord = 1;
@@ -2244,9 +2265,31 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
break;
default:
- flagerr:
- zerr("error in flags");
- return NULL;
+ flagerr:
+ {
+ /*
+ * We're trying to output the string that failed to
+ * parse and the offset of the parse error within that.
+ *
+ * The string is *str. It hasn't been changed since
+ * entry to this function, I think, except that the
+ * first non-variable-declaration line in this function
+ * (currently the 238th line in this function)
+ * writes a NUL to the first place in *str, so we'll
+ * compensate by outputting the dollar sign manually.
+ */
+ char *str_copy_for_output = dupstring(*str + 1);
+
+ /*
+ * Convert to a 1-based offset, because the shell
+ * language is 1-based by default.
+ */
+ zlong offset = s - *str + 1;
+
+ untokenize(str_copy_for_output);
+ zerr("error in flags near position %z in '$%s'", offset, str_copy_for_output);
+ return NULL;
+ }
}
}
s++;
@@ -2526,7 +2569,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
* Handle the (t) flag: value now becomes the type
* information for the parameter.
*/
- if (v && v->pm && !(v->pm->node.flags & PM_UNSET)) {
+ if (v && v->pm && ((v->pm->node.flags & PM_DECLARED) ||
+ !(v->pm->node.flags & PM_UNSET))) {
int f = v->pm->node.flags;
switch (PM_TYPE(f)) {
@@ -2781,7 +2825,6 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
}
if (bct) {
- noclosebrace:
zerr("closing brace expected");
return NULL;
}
@@ -2800,7 +2843,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
c == '#' || c == Pound ||
c == '?' || c == Quest ||
c == '/')) {
-
+ int eglob = isset(EXTENDEDGLOB);
/*
* Default index is 1 if no (I) or (I) gave zero. But
* why don't we set the default explicitly at the start
@@ -2822,9 +2865,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
char *ptr;
/*
* previous flags are irrelevant, except for (S) which
- * indicates shortest substring; else look for longest.
+ * indicates shortest substring; else look for longest,
+ * and (*) which temporarily enables extended globbing.
*/
- flags = (flags & SUB_SUBSTR) ? 0 : SUB_LONG;
+ flags = ((flags & SUB_SUBSTR) ? 0 : SUB_LONG)|(flags & SUB_EGLOB);
if ((c = *s) == '/') {
/* doubled, so replace all occurrences */
flags |= SUB_GLOBAL;
@@ -2937,9 +2981,12 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
}
break;
case ':':
- /* this must be `::=', unconditional assignment */
- if (*s != '=' && *s != Equals)
- goto noclosebrace;
+ /* this could be either `::=', unconditional assignment
+ * or a ${name:offset:length} with an empty offset */
+ if (*s != '=' && *s != Equals) {
+ s -= 1;
+ goto colonsubscript;
+ }
vunset = 1;
s++;
/* Fall through */
@@ -3123,7 +3170,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
for (ap = aval; *ap; ap++) {
untokenize(*ap);
}
+ if (flags & SUB_EGLOB)
+ opts[EXTENDEDGLOB] = 1;
getmatcharr(&aval, s, flags, flnum, replstr);
+ opts[EXTENDEDGLOB] = eglob;
} else {
if (vunset) {
if (vunset > 0 && unset(UNSET)) {
@@ -3138,7 +3188,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
copied = 1;
untokenize(val);
}
+ if (flags & SUB_EGLOB)
+ opts[EXTENDEDGLOB] = 1;
getmatch(&val, s, flags, flnum, replstr);
+ opts[EXTENDEDGLOB] = eglob;
}
break;
}
@@ -3282,6 +3335,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
* if there isn't a trailing modifier? Why don't we do this
* e.g. when we handle the ${(t)...} flag?
*/
+colonsubscript:
if (chkset) {
val = dupstring(vunset ? "0" : "1");
isarr = 0;
@@ -3314,20 +3368,31 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
return NULL;
}
if (*check_offset2) {
+ char *nextp;
check_offset = check_colon_subscript(check_offset2 + 1,
- &check_offset2);
- if (*check_offset2 && *check_offset2 != ':') {
- zerr("invalid length: %s", check_offset);
- return NULL;
- }
+ &nextp);
if (check_offset) {
+ check_offset2 = nextp;
+ if (*check_offset2 && *check_offset2 != ':') {
+ zerr("invalid length: %s", check_offset);
+ return NULL;
+ }
length = mathevali(check_offset);
length_set = 1;
if (errflag)
return NULL;
}
}
- if (isarr) {
+ /*
+ * We've got :OFFSET (and :LENGTH).
+ * If aval is non-NULL but isarr is 0, PARAM is (probably)
+ * an array but quoted like "${PARAM:OFFSET}". We apply
+ * :OFFSET as array index (as if it is not quoted). We will
+ * join them later (search for quoted_array_with_offset).
+ */
+ if (aval && !isarr)
+ quoted_array_with_offset = 1;
+ if (isarr || quoted_array_with_offset) {
int alen, count;
char **srcptr, **dstptr, **newarr;
@@ -3572,9 +3637,9 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
* exception is that ${name:-word} and ${name:+word} will have already
* done any requested splitting of the word value with quoting preserved.
*/
- if (ssub || spbreak || spsep || sep) {
+ if (ssub || spbreak || spsep || sep || quoted_array_with_offset) {
int force_split = !ssub && (spbreak || spsep);
- if (isarr) {
+ if (isarr || quoted_array_with_offset) {
/* sep non-null here means F or j flag, force join */
if (nojoin == 0 || sep) {
val = sepjoin(aval, sep, 1);
@@ -4399,7 +4464,7 @@ modify(char **str, char **ptr, int inbrace)
chabspath(&copy);
break;
case 'A':
- chrealpath(&copy);
+ chrealpath(&copy, 'A', 1);
break;
case 'c':
{
@@ -4485,7 +4550,7 @@ modify(char **str, char **ptr, int inbrace)
chabspath(str);
break;
case 'A':
- chrealpath(str);
+ chrealpath(str, 'A', 1);
break;
case 'c':
{
diff --git a/Src/text.c b/Src/text.c
index 69530ae79..5cd7685fd 100644
--- a/Src/text.c
+++ b/Src/text.c
@@ -335,6 +335,8 @@ getjobtext(Eprog prog, Wordcode c)
tlim = tptr + JOBTEXTSIZE - 1;
tjob = 1;
gettext2(&s);
+ if (tptr[-1] == Meta)
+ --tptr;
*tptr = '\0';
freeeprog(prog); /* mark as unused */
untokenize(jbuf);
@@ -600,7 +602,7 @@ gettext2(Estate state)
n->u._funcdef.end = end;
n->u._funcdef.nargs = nargs;
state->strs += *state->pc;
- state->pc += 3;
+ state->pc += 4;
}
} else {
state->strs = s->u._funcdef.strs;
diff --git a/Src/utils.c b/Src/utils.c
index f5667f389..62bd3e602 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -130,6 +130,7 @@ set_widearray(char *mb_array, Widechar_array wca)
%l const char *, int C string of given length (null not required)
%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')
@@ -331,6 +332,14 @@ zerrmsg(FILE *file, const char *fmt, va_list ap)
num = va_arg(ap, int);
fprintf(file, "%d", num);
break;
+ case 'z':
+ {
+ zlong znum = va_arg(ap, zlong);
+ char buf[DIGBUFSIZE];
+ convbase(buf, znum, 10);
+ fputs(buf, file);
+ break;
+ }
case '%':
putc('%', file);
break;
@@ -429,7 +438,6 @@ putshout(int c)
return 0;
}
-#ifdef MULTIBYTE_SUPPORT
/*
* Turn a character into a visible representation thereof. The visible
* string is put together in a static buffer, and this function returns
@@ -515,62 +523,6 @@ nicechar(int c)
return nicechar_sel(c, 0);
}
-#else /* MULTIBYTE_SUPPORT */
-
-/**/
-mod_export char *
-nicechar(int c)
-{
- static char buf[10];
- char *s = buf;
- c &= 0xff;
- if (ZISPRINT(c))
- goto done;
- if (c & 0x80) {
- if (isset(PRINTEIGHTBIT))
- goto done;
- *s++ = '\\';
- *s++ = 'M';
- *s++ = '-';
- c &= 0x7f;
- if(ZISPRINT(c))
- goto done;
- }
- if (c == 0x7f) {
- *s++ = '\\';
- *s++ = 'C';
- *s++ = '-';
- c = '?';
- } else if (c == '\n') {
- *s++ = '\\';
- c = 'n';
- } else if (c == '\t') {
- *s++ = '\\';
- c = 't';
- } else if (c < 0x20) {
- *s++ = '\\';
- *s++ = 'C';
- *s++ = '-';
- c += 0x40;
- }
- done:
- /*
- * The resulting string is still metafied, so check if
- * we are returning a character in the range that needs metafication.
- * This can't happen if the character is printed "nicely", so
- * this results in a maximum of two bytes total (plus the null).
- */
- if (imeta(c)) {
- *s++ = Meta;
- *s++ = c ^ 32;
- } else
- *s++ = c;
- *s = 0;
- return buf;
-}
-
-#endif /* MULTIBYTE_SUPPORT */
-
/*
* Return 1 if nicechar() would reformat this character.
*/
@@ -711,7 +663,7 @@ wcs_nicechar_sel(wchar_t c, size_t *widthp, char **swidep, int quotable)
if (widthp)
*widthp = 6;
} else {
- strcpy(buf, nicechar((int)c));
+ strcpy(buf, nicechar_sel((int)c, quotable));
/*
* There may be metafied characters from nicechar(),
* so compute width and end position independently.
@@ -771,7 +723,7 @@ mod_export int is_wcs_nicechar(wchar_t c)
if (c == 0x7f || c == L'\n' || c == L'\t' || c < 0x20)
return 1;
if (c >= 0x80) {
- return (c >= 0x100);
+ return (c >= 0x100 || is_nicechar((int)c));
}
}
return 0;
@@ -910,11 +862,14 @@ slashsplit(char *s)
return r;
}
-/* expands symlinks and .. or . expressions */
+/* expands .. or . expressions and one level of symlinks
+ *
+ * Puts the result in the global "xbuf"
+ */
/**/
static int
-xsymlinks(char *s, int full)
+xsymlinks(char *s)
{
char **pp, **opp;
char xbuf2[PATH_MAX*3+1], xbuf3[PATH_MAX*2+1];
@@ -963,7 +918,7 @@ xsymlinks(char *s, int full)
} else {
ret = 1;
metafy(xbuf3, t0, META_NOALLOC);
- if (!full) {
+ {
/*
* If only one expansion requested, ensure the
* full path is in xbuf.
@@ -998,17 +953,6 @@ xsymlinks(char *s, int full)
*/
break;
}
- if (*xbuf3 == '/') {
- strcpy(xbuf, "");
- if (xsymlinks(xbuf3 + 1, 1) < 0)
- ret = -1;
- else
- xbuflen = strlen(xbuf);
- } else
- if (xsymlinks(xbuf3, 1) < 0)
- ret = -1;
- else
- xbuflen = strlen(xbuf);
}
}
freearray(opp);
@@ -1023,17 +967,17 @@ xsymlinks(char *s, int full)
*/
/**/
-char *
+mod_export char *
xsymlink(char *s, int heap)
{
if (*s != '/')
return NULL;
*xbuf = '\0';
- if (xsymlinks(s + 1, 1) < 0)
+ if (!chrealpath(&s, 'P', heap)) {
zwarn("path expansion failed, using root directory");
- if (!*xbuf)
return heap ? dupstring("/") : ztrdup("/");
- return heap ? dupstring(xbuf) : ztrdup(xbuf);
+ }
+ return s;
}
/**/
@@ -1041,12 +985,12 @@ void
print_if_link(char *s, int all)
{
if (*s == '/') {
- *xbuf = '\0';
if (all) {
char *start = s + 1;
char xbuflink[PATH_MAX+1];
+ *xbuf = '\0';
for (;;) {
- if (xsymlinks(start, 0) > 0) {
+ if (xsymlinks(start) > 0) {
printf(" -> ");
zputs(*xbuf ? xbuf : "/", stdout);
if (!*xbuf)
@@ -1059,8 +1003,11 @@ print_if_link(char *s, int all)
}
}
} else {
- if (xsymlinks(s + 1, 1) > 0)
- printf(" -> "), zputs(*xbuf ? xbuf : "/", stdout);
+ if (chrealpath(&s, 'P', 0)) {
+ printf(" -> ");
+ zputs(*s ? s : "/", stdout);
+ zsfree(s);
+ }
}
}
}
@@ -1104,7 +1051,7 @@ substnamedir(char *s)
/* Returns the current username. It caches the username *
* and uid to try to avoid requerying the password files *
- * or NIS/NIS+ database. */
+ * or other source. */
/**/
uid_t cached_uid;
@@ -1115,7 +1062,7 @@ char *cached_username;
char *
get_username(void)
{
-#ifdef HAVE_GETPWUID
+#ifdef USE_GETPWUID
struct passwd *pswd;
uid_t current_uid;
@@ -1128,9 +1075,9 @@ get_username(void)
else
cached_username = ztrdup("");
}
-#else /* !HAVE_GETPWUID */
+#else /* !USE_GETPWUID */
cached_uid = getuid();
-#endif /* !HAVE_GETPWUID */
+#endif /* !USE_GETPWUID */
return cached_username;
}
@@ -1306,7 +1253,7 @@ getnameddir(char *name)
return str;
}
-#ifdef HAVE_GETPWNAM
+#ifdef USE_GETPWNAM
{
/* Retrieve an entry from the password table/database for this user. */
struct passwd *pw;
@@ -1322,7 +1269,7 @@ getnameddir(char *name)
return dupstring(pw->pw_dir);
}
}
-#endif /* HAVE_GETPWNAM */
+#endif /* USE_GETPWNAM */
/* There are no more possible sources of directory names, so give up. */
return NULL;
@@ -1374,6 +1321,9 @@ delprepromptfn(voidvoidfnptr_t func)
{
LinkNode ln;
+ if (!prepromptfns)
+ return;
+
for (ln = firstnode(prepromptfns); ln; ln = nextnode(ln)) {
Prepromptfn ppdat = (Prepromptfn)getdata(ln);
if (ppdat->func == func) {
@@ -1487,14 +1437,9 @@ deltimedfn(voidvoidfnptr_t func)
/**/
time_t lastmailcheck;
-/* the last time we checked the people in the WATCH variable */
-
-/**/
-time_t lastwatch;
-
/*
* Call a function given by "name" with optional arguments
- * "lnklist". If these are present the first argument is the function name.
+ * "lnklst". If these are present the first argument is the function name.
*
* If "arrayp" is not zero, we also look through
* the array "name"_functions and execute functions found there.
@@ -1523,6 +1468,10 @@ callhookfunc(char *name, LinkList lnklst, int arrayp, int *retval)
incompfunc = 0;
if ((shfunc = getshfunc(name))) {
+ if (!lnklst) {
+ lnklst = newlinklist();
+ addlinknode(lnklst, name);
+ }
ret = doshfunc(shfunc, lnklst, 1);
stat = 0;
}
@@ -1535,10 +1484,16 @@ callhookfunc(char *name, LinkList lnklst, int arrayp, int *retval)
memcpy(arrnam + namlen, HOOK_SUFFIX, HOOK_SUFFIX_LEN);
if ((arrptr = getaparam(arrnam))) {
+ char **argarr = lnklst ? hlinklist2array(lnklst, 0) : NULL;
arrptr = arrdup(arrptr);
for (; *arrptr; arrptr++) {
if ((shfunc = getshfunc(*arrptr))) {
- int newret = doshfunc(shfunc, lnklst, 1);
+ int newret, i = 1;
+ LinkList arg0 = newlinklist();
+ addlinknode(arg0, *arrptr);
+ while (argarr && argarr[i])
+ addlinknode(arg0, argarr[i++]);
+ newret = doshfunc(shfunc, arg0, 1);
if (!ret)
ret = newret;
stat = 0;
@@ -1620,17 +1575,6 @@ preprompt(void)
if (errflag)
return;
- /* If WATCH is set, then check for the *
- * specified login/logout events. */
- if (watch) {
- if ((int) difftime(time(NULL), lastwatch) > getiparam("LOGCHECK")) {
- dowatch();
- lastwatch = time(NULL);
- }
- }
- if (errflag)
- return;
-
/* Check mail */
currentmailcheck = time(NULL);
if (mailcheck &&
@@ -2745,6 +2689,42 @@ read_poll(int fd, int *readchar, int polltty, zlong microseconds)
}
/*
+ * Return the difference between 2 times, given as struct timespec*,
+ * expressed in microseconds, as a long. If the difference doesn't fit
+ * into a long, return LONG_MIN or LONG_MAX so that the times can still
+ * be compared.
+ *
+ * Note: returns a long rather than a zlong because zsleep() below
+ * takes a long.
+ */
+
+/**/
+long
+timespec_diff_us(const struct timespec *t1, const struct timespec *t2)
+{
+ int reverse = (t1->tv_sec > t2->tv_sec);
+ time_t diff_sec;
+ long diff_usec, max_margin, res;
+
+ /* Don't just subtract t2-t1 because time_t might be unsigned. */
+ diff_sec = (reverse ? t1->tv_sec - t2->tv_sec : t2->tv_sec - t1->tv_sec);
+ if (diff_sec > LONG_MAX / 1000000L) {
+ goto overflow;
+ }
+ res = diff_sec * 1000000L;
+ max_margin = LONG_MAX - res;
+ diff_usec = (reverse ?
+ t1->tv_nsec - t2->tv_nsec : t2->tv_nsec - t1->tv_nsec
+ ) / 1000;
+ if (diff_usec <= max_margin) {
+ res += diff_usec;
+ return (reverse ? -res : res);
+ }
+ overflow:
+ return (reverse ? LONG_MIN : LONG_MAX);
+}
+
+/*
* Sleep for the given number of microseconds --- must be within
* range of a long at the moment, but this is only used for
* limited internal purposes.
@@ -3088,11 +3068,13 @@ spckword(char **s, int hist, int cmd, int ask)
int preflen = 0;
int autocd = cmd && isset(AUTOCD) && strcmp(*s, ".") && strcmp(*s, "..");
- if ((histdone & HISTFLAG_NOEXEC) || **s == '-' || **s == '%')
+ if (!(*s)[0] || !(*s)[1])
return;
- if (!strcmp(*s, "in"))
+ if ((histdone & HISTFLAG_NOEXEC) ||
+ /* Leading % is a job, else leading hyphen is an option */
+ (cmd ? **s == '%' : (**s == '-' || **s == Dash)))
return;
- if (!(*s)[0] || !(*s)[1])
+ if (!strcmp(*s, "in"))
return;
if (cmd) {
if (shfunctab->getnode(shfunctab, *s) ||
@@ -3111,8 +3093,12 @@ spckword(char **s, int hist, int cmd, int ask)
if (*t == Tilde || *t == Equals || *t == String)
t++;
for (; *t; t++)
- if (itok(*t))
- return;
+ if (itok(*t)) {
+ if (*t == Dash)
+ *t = '-';
+ else
+ return;
+ }
best = NULL;
for (t = *s; *t; t++)
if (*t == '/')
@@ -3556,6 +3542,17 @@ strftimehandling:
return buf - origbuf;
}
+/*
+ * Return a string consisting of the elements of 'arr' joined by the character
+ * 'delim', which will be metafied if necessary. The string will be allocated
+ * on the heap iff 'heap'.
+ *
+ * Comparable to:
+ *
+ * char metafied_delim[] = { Meta, delim ^ 32, '\0' };
+ * sepjoin(arr, metafied_delim, heap)
+ */
+
/**/
mod_export char *
zjoin(char **arr, int delim, int heap)
@@ -3854,10 +3851,12 @@ wordcount(char *s, char *sep, int mul)
/*
* 's' is a NULL-terminated array of strings.
- * 'sep' is a string.
+ * 'sep' is a string, or NULL to split on ${IFS[1]}.
*
* Return a string consisting of the elements of 's' joined by 'sep',
* allocated on the heap iff 'heap'.
+ *
+ * See also zjoin().
*/
/**/
@@ -4268,7 +4267,7 @@ wcsitype(wchar_t c, int itype)
} else {
switch (itype) {
case IIDENT:
- if (!isset(POSIXIDENTIFIERS))
+ if (isset(POSIXIDENTIFIERS))
return 0;
return iswalnum(c);
@@ -5197,26 +5196,11 @@ zputs(char const *s, FILE *stream)
mod_export char *
nicedup(char const *s, int heap)
{
- int c, len = strlen(s) * 5 + 1;
- VARARR(char, buf, len);
- char *p = buf, *n;
+ char *retstr;
- while ((c = *s++)) {
- if (itok(c)) {
- if (c <= Comma)
- c = ztokens[c - Pound];
- else
- continue;
- }
- if (c == Meta)
- c = *s++ ^ 32;
- /* The result here is metafied */
- n = nicechar(c);
- while(*n)
- *p++ = *n++;
- }
- *p = '\0';
- return heap ? dupstring(buf) : ztrdup(buf);
+ (void)sb_niceformat(s, NULL, &retstr, heap ? NICEFLAG_HEAP : 0);
+
+ return retstr;
}
#endif
@@ -5235,20 +5219,7 @@ nicedupstring(char const *s)
mod_export int
nicezputs(char const *s, FILE *stream)
{
- int c;
-
- while ((c = *s++)) {
- if (itok(c)) {
- if (c <= Comma)
- c = ztokens[c - Pound];
- else
- continue;
- }
- if (c == Meta)
- c = *s++ ^ 32;
- if(zputs(nicechar(c), stream) < 0)
- return EOF;
- }
+ sb_niceformat(s, stream, NULL, 0);
return 0;
}
@@ -5737,7 +5708,7 @@ mb_charlenconv(const char *s, int slen, wint_t *wcp)
}
/**/
-#else
+#else /* MULTIBYTE_SUPPORT */
/* Simple replacement for mb_metacharlenconv */
@@ -5777,6 +5748,121 @@ charlenconv(const char *x, int len, int *c)
return 1;
}
+/*
+ * Non-multibyte version of mb_niceformat() above. Same basic interface.
+ */
+
+/**/
+mod_export size_t
+sb_niceformat(const char *s, FILE *stream, char **outstrp, int flags)
+{
+ size_t l = 0, newl;
+ int umlen, outalloc, outleft;
+ char *ums, *ptr, *eptr, *fmt, *outstr, *outptr;
+
+ if (outstrp) {
+ outleft = outalloc = 2 * strlen(s);
+ outptr = outstr = zalloc(outalloc);
+ } else {
+ outleft = outalloc = 0;
+ outptr = outstr = NULL;
+ }
+
+ ums = ztrdup(s);
+ /*
+ * is this necessary at this point? niceztrlen does this
+ * but it's used in lots of places. however, one day this may
+ * be, too.
+ */
+ untokenize(ums);
+ ptr = unmetafy(ums, &umlen);
+ eptr = ptr + umlen;
+
+ while (ptr < eptr) {
+ int c = STOUC(*ptr);
+ if (c == '\'' && (flags & NICEFLAG_QUOTE)) {
+ fmt = "\\'";
+ newl = 2;
+ }
+ else if (c == '\\' && (flags & NICEFLAG_QUOTE)) {
+ fmt = "\\\\";
+ newl = 2;
+ }
+ else {
+ fmt = nicechar_sel(c, flags & NICEFLAG_QUOTE);
+ newl = 1;
+ }
+
+ ++ptr;
+ l += newl;
+
+ if (stream)
+ zputs(fmt, stream);
+ if (outstr) {
+ /* Append to output string */
+ int outlen = strlen(fmt);
+ if (outlen >= outleft) {
+ /* Reallocate to twice the length */
+ int outoffset = outptr - outstr;
+
+ outleft += outalloc;
+ outalloc *= 2;
+ outstr = zrealloc(outstr, outalloc);
+ outptr = outstr + outoffset;
+ }
+ memcpy(outptr, fmt, outlen);
+ /* Update start position */
+ outptr += outlen;
+ /* Update available bytes */
+ outleft -= outlen;
+ }
+ }
+
+ free(ums);
+ if (outstrp) {
+ *outptr = '\0';
+ /* Use more efficient storage for returned string */
+ if (flags & NICEFLAG_NODUP)
+ *outstrp = outstr;
+ else {
+ *outstrp = (flags & NICEFLAG_HEAP) ? dupstring(outstr) :
+ ztrdup(outstr);
+ free(outstr);
+ }
+ }
+
+ return l;
+}
+
+/*
+ * Return 1 if sb_niceformat() would reformat this string, else 0.
+ */
+
+/**/
+mod_export int
+is_sb_niceformat(const char *s)
+{
+ int umlen, ret = 0;
+ char *ums, *ptr, *eptr;
+
+ ums = ztrdup(s);
+ untokenize(ums);
+ ptr = unmetafy(ums, &umlen);
+ eptr = ptr + umlen;
+
+ while (ptr < eptr) {
+ if (is_nicechar(*ptr)) {
+ ret = 1;
+ break;
+ }
+ ++ptr;
+ }
+
+ free(ums);
+
+ return ret;
+}
+
/**/
#endif /* MULTIBYTE_SUPPORT */
@@ -5853,8 +5939,11 @@ zexpandtabs(const char *s, int len, int width, int startpos, FILE *fout,
memset(&mbs, 0, sizeof(mbs));
s++;
len--;
- } else if (ret == MB_INCOMPLETE) {
+ } else if (ret == MB_INCOMPLETE ||
/* incomplete at end --- assume likewise, best we've got */
+ ret == 0) {
+ /* NUL character returns 0, which would loop infinitely, so advance
+ * one byte in this case too */
s++;
len--;
} else {
@@ -6307,6 +6396,22 @@ quotedzputs(char const *s, FILE *stream)
return outstr;
}
}
+#else
+ if (is_sb_niceformat(s)){
+ if (stream) {
+ fputs("$'", stream);
+ sb_niceformat(s, stream, NULL, NICEFLAG_QUOTE);
+ fputc('\'', stream);
+ return NULL;
+ } else {
+ char *substr;
+ sb_niceformat(s, NULL, &substr, NICEFLAG_QUOTE|NICEFLAG_NODUP);
+ outstr = (char *)zhalloc(4 + strlen(substr));
+ sprintf(outstr, "$'%s'", substr);
+ free(substr);
+ return outstr;
+ }
+ }
#endif /* MULTIBYTE_SUPPORT */
if (!hasspecial(s)) {
@@ -6635,13 +6740,21 @@ ucs4toutf8(char *dest, unsigned int wval)
*
* The return value is unmetafied unless GETKEY_DOLLAR_QUOTE is
* in use.
+ *
+ * If GETKEY_SINGLE_CHAR is set in how, a next character in the given
+ * string is parsed, and the character code for it is returned in misc.
+ * The return value of the function is a pointer to the byte in the
+ * given string from where the next parsing should start. If the next
+ * character can't be found then NULL is returned.
+ * CAUTION: Currently, GETKEY_SINGLE_CHAR can be used only via
+ * GETKEYS_MATH. Other use of it may cause trouble.
*/
/**/
mod_export char *
getkeystring(char *s, int *len, int how, int *misc)
{
- char *buf, tmp[1];
+ char *buf = NULL, tmp[1];
char *t, *tdest = NULL, *u = NULL, *sstart = s, *tbuf = NULL;
char svchar = '\0';
int meta = 0, control = 0, ignoring = 0;
@@ -6667,9 +6780,11 @@ getkeystring(char *s, int *len, int how, int *misc)
DPUTS((how & (GETKEY_DOLLAR_QUOTE|GETKEY_SINGLE_CHAR)) ==
(GETKEY_DOLLAR_QUOTE|GETKEY_SINGLE_CHAR),
"BUG: incompatible options in getkeystring");
+ DPUTS((how & GETKEY_SINGLE_CHAR) && (how != GETKEYS_MATH),
+ "BUG: unsupported options in getkeystring");
if (how & GETKEY_SINGLE_CHAR)
- t = buf = tmp;
+ t = tmp;
else {
/* Length including terminating NULL */
int maxlen = 1;
@@ -7103,13 +7218,20 @@ getkeystring(char *s, int *len, int how, int *misc)
*/
DPUTS((how & (GETKEY_DOLLAR_QUOTE|GETKEY_UPDATE_OFFSET)) ==
GETKEY_DOLLAR_QUOTE, "BUG: unterminated $' substitution");
- *t = '\0';
- if (how & GETKEY_DOLLAR_QUOTE)
- *tdest = '\0';
- if (how & GETKEY_SINGLE_CHAR)
+
+ if (how & GETKEY_SINGLE_CHAR) {
+ /* couldn't find a character */
*misc = 0;
- else
- *len = ((how & GETKEY_DOLLAR_QUOTE) ? tdest : t) - buf;
+ return NULL;
+ }
+ if (how & GETKEY_DOLLAR_QUOTE) {
+ *tdest = '\0';
+ *len = tdest - buf;
+ }
+ else {
+ *t = '\0';
+ *len = t - buf;
+ }
return buf;
}
diff --git a/Src/zsh.h b/Src/zsh.h
index 834142895..40f9ea537 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -832,22 +832,59 @@ struct estate {
char *strs; /* strings from prog */
};
+/*
+ * A binary tree of strings.
+ *
+ * Refer to the "Word code." comment at the top of Src/parse.c for details.
+ */
typedef struct eccstr *Eccstr;
-
struct eccstr {
+ /* Child pointers. */
Eccstr left, right;
+
+ /* String; pointer into to estate::strs. */
char *str;
- wordcode offs, aoffs;
+
+ /* Wordcode of a long string, as described in the Src/parse.c comment. */
+ wordcode offs;
+
+ /* Raw memory offset of str in estate::strs. */
+ wordcode aoffs;
+
+ /*
+ * ### The number of starts and ends of function definitions up to this point.
+ *
+ * String reuse may only happen between strings that have the same "nfunc" value.
+ */
int nfunc;
+
+ /* Hash of str. */
int hashval;
};
-#define EC_NODUP 0
-#define EC_DUP 1
-#define EC_DUPTOK 2
+/*
+ * Values for the "dup" parameter to ecgetstr().
+ */
+enum ec_dup_t {
+ /*
+ * Make no promises about how the return value is allocated, except that
+ * the caller does not need to explicitly free it. It might be heap allocated,
+ * a static string, or anything in between.
+ */
+ EC_NODUP = 0,
-#define WC_CODEBITS 5
+ /* Allocate the return value from the heap. */
+ EC_DUP = 1,
+
+ /*
+ * If the string contains tokens (as indicated by the least significant bit
+ * of the wordcode), behave as EC_DUP; otherwise, as EC_NODUP.
+ */
+ EC_DUPTOK = 2
+};
+/* See comment at the top of Src/parse.c for details. */
+#define WC_CODEBITS 5
#define wc_code(C) ((C) & ((wordcode) ((1 << WC_CODEBITS) - 1)))
#define wc_data(C) ((C) >> WC_CODEBITS)
#define wc_bdata(D) ((D) << WC_CODEBITS)
@@ -876,7 +913,11 @@ struct eccstr {
#define WC_AUTOFN 20
#define WC_TRY 21
-/* increment as necessary */
+/*
+ * Increment as necessary.
+ *
+ * If this exceeds 31, increment WC_CODEBITS.
+ */
#define WC_COUNT 22
#define WCB_END() wc_bld(WC_END, 0)
@@ -1156,7 +1197,10 @@ typedef void (*ScanTabFunc) _((HashTable, ScanFunc, int));
typedef void (*PrintTableStats) _((HashTable));
-/* hash table for standard open hashing */
+/* Hash table for standard open hashing. Instances of struct hashtable can be *
+ * created only by newhashtable(). In fact, this function creates an instance *
+ * of struct hashtableimpl, which is made of struct hashtable (public part) *
+ * and additional data members that are only accessible from hashtable.c. */
struct hashtable {
/* HASHTABLE DATA */
@@ -1180,10 +1224,6 @@ struct hashtable {
FreeNodeFunc freenode; /* pointer to function to free a node */
ScanFunc printnode; /* pointer to function to print a node */
ScanTabFunc scantab; /* pointer to function to scan table */
-
-#ifdef HASHTABLE_INTERNAL_MEMBERS
- HASHTABLE_INTERNAL_MEMBERS /* internal use in hashtable.c */
-#endif
};
/* generic hash table node */
@@ -1408,7 +1448,7 @@ struct builtin {
int minargs; /* minimum number of arguments */
int maxargs; /* maximum number of arguments, or -1 for no limit */
int funcid; /* xbins (see above) for overloaded handlerfuncs */
- char *optstr; /* string of legal options */
+ char *optstr; /* string of legal options (see execbuiltin()) */
char *defopts; /* options set by default for overloaded handlerfuncs */
};
@@ -1889,8 +1929,10 @@ struct tieddata {
made read-only by the user */
#define PM_READONLY_SPECIAL (PM_SPECIAL|PM_READONLY|PM_RO_BY_DESIGN)
#define PM_DONTIMPORT (1<<22) /* do not import this variable */
+#define PM_DECLARED (1<<22) /* explicitly named with typeset */
#define PM_RESTRICTED (1<<23) /* cannot be changed in restricted mode */
#define PM_UNSET (1<<24) /* has null value */
+#define PM_DEFAULTED (PM_DECLARED|PM_UNSET)
#define PM_REMOVABLE (1<<25) /* special can be removed from paramtab */
#define PM_AUTOLOAD (1<<26) /* autoloaded from module */
#define PM_NORESTORE (1<<27) /* do not restore value of local special */
@@ -1951,6 +1993,7 @@ struct tieddata {
#define SUB_START 0x1000 /* force match at start with SUB_END
* and no SUB_SUBSTR */
#define SUB_LIST 0x2000 /* no substitution, return list of matches */
+#define SUB_EGLOB 0x4000 /* use extended globbing in patterns */
/*
* Structure recording multiple matches inside a test string.
@@ -2346,6 +2389,7 @@ enum {
BSDECHO,
CASEGLOB,
CASEMATCH,
+ CASEPATHS,
CBASES,
CDABLEVARS,
CDSILENT,
@@ -2354,6 +2398,7 @@ enum {
CHECKJOBS,
CHECKRUNNINGJOBS,
CLOBBER,
+ CLOBBEREMPTY,
APPENDCREATE,
COMBININGCHARS,
COMPLETEALIASES,
@@ -2484,6 +2529,7 @@ enum {
SHNULLCMD,
SHOPTIONLETTERS,
SHORTLOOPS,
+ SHORTREPEAT,
SHWORDSPLIT,
SINGLECOMMAND,
SINGLELINEZLE,
@@ -2492,6 +2538,7 @@ enum {
TRANSIENTRPROMPT,
TRAPSASYNC,
TYPESETSILENT,
+ TYPESETTOUNSET,
UNSET,
VERBOSE,
VIMODE,
@@ -2683,11 +2730,6 @@ struct ttyinfo {
/* Bits to shift the background colour */
#define TXT_ATTR_BG_COL_SHIFT (40)
-/* Flag to use termcap AF sequence to set colour, if available */
-#define TXT_ATTR_FG_TERMCAP 0x1000
-/* Flag to use termcap AB sequence to set colour, if available */
-#define TXT_ATTR_BG_TERMCAP 0x2000
-
/* Flag to indicate that foreground is a 24-bit colour */
#define TXT_ATTR_FG_24BIT 0x4000
/* Flag to indicate that background is a 24-bit colour */
@@ -2696,16 +2738,15 @@ struct ttyinfo {
/* 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_TERMCAP|TXT_ATTR_BG_TERMCAP|\
TXT_ATTR_FG_24BIT|TXT_ATTR_BG_24BIT)
/* Mask out everything to do with setting a foreground colour */
#define TXT_ATTR_FG_ON_MASK \
- (TXTFGCOLOUR|TXT_ATTR_FG_COL_MASK|TXT_ATTR_FG_TERMCAP|TXT_ATTR_FG_24BIT)
+ (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 \
- (TXTBGCOLOUR|TXT_ATTR_BG_COL_MASK|TXT_ATTR_BG_TERMCAP|TXT_ATTR_BG_24BIT)
+ (TXTBGCOLOUR|TXT_ATTR_BG_COL_MASK|TXT_ATTR_BG_24BIT)
/* Mask out everything to do with activating colours */
#define TXT_ATTR_COLOUR_ON_MASK \
@@ -2971,17 +3012,18 @@ enum {
SORTIT_ANYOLDHOW = 0, /* Defaults */
SORTIT_IGNORING_CASE = 1,
SORTIT_NUMERICALLY = 2,
- SORTIT_BACKWARDS = 4,
+ SORTIT_NUMERICALLY_SIGNED = 4,
+ SORTIT_BACKWARDS = 8,
/*
* Ignore backslashes that quote another character---which may
* be another backslash; the second backslash is active.
*/
- SORTIT_IGNORING_BACKSLASHES = 8,
+ SORTIT_IGNORING_BACKSLASHES = 16,
/*
* Ignored by strmetasort(); used by paramsubst() to indicate
* there is some sorting to do.
*/
- SORTIT_SOMEHOW = 16,
+ SORTIT_SOMEHOW = 32,
};
/*
@@ -3235,14 +3277,15 @@ enum zexit_t {
#define AFTERTRAPHOOK (zshhooks + 2)
#define GETCOLORATTR (zshhooks + 3)
-#ifdef MULTIBYTE_SUPPORT
-/* Final argument to mb_niceformat() */
+/* Final argument to [ms]b_niceformat() */
enum {
NICEFLAG_HEAP = 1, /* Heap allocation where needed */
NICEFLAG_QUOTE = 2, /* Result will appear in $'...' */
NICEFLAG_NODUP = 4, /* Leave allocated */
};
+#ifdef MULTIBYTE_SUPPORT
+
/* Metafied input */
#define nicezputs(str, outs) (void)mb_niceformat((str), (outs), NULL, 0)
#define MB_METACHARINIT() mb_charinit()
diff --git a/Src/zsh.mdd b/Src/zsh.mdd
index 9bcaccae5..da8d58322 100644
--- a/Src/zsh.mdd
+++ b/Src/zsh.mdd
@@ -13,7 +13,7 @@ objects="builtin.o compat.o cond.o context.o \
exec.o glob.o hashtable.o hashnameddir.o \
hist.o init.o input.o jobs.o lex.o linklist.o loop.o math.o \
mem.o module.o options.o params.o parse.o pattern.o prompt.o signals.o \
-signames.o sort.o string.o subst.o text.o utils.o watch.o \
+signames.o sort.o string.o subst.o text.o utils.o \
openssh_bsd_setres_id.o"
headers="../config.h zsh_system.h zsh.h sigcount.h signals.h \
diff --git a/Src/zsh_system.h b/Src/zsh_system.h
index 161b073b4..6f4efce96 100644
--- a/Src/zsh_system.h
+++ b/Src/zsh_system.h
@@ -235,16 +235,8 @@ char *alloca _((size_t));
# include <errno.h>
#endif
-#ifdef TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# ifdef HAVE_SYS_TIME_H
-# include <sys/time.h>
-# else
-# include <time.h>
-# endif
-#endif
+#include <sys/time.h>
+#include <time.h>
/* This is needed by some old SCO unices */
#if !defined(HAVE_STRUCT_TIMEZONE) && !defined(ZSH_OOT_MODULE)
@@ -279,16 +271,7 @@ struct timespec {
# include <sys/times.h>
#endif
-#if STDC_HEADERS || HAVE_STRING_H
# include <string.h>
-/* An ANSI string.h and pre-ANSI memory.h might conflict. */
-# if !STDC_HEADERS && HAVE_MEMORY_H
-# include <memory.h>
-# endif /* not STDC_HEADERS and HAVE_MEMORY_H */
-#else /* not STDC_HEADERS and not HAVE_STRING_H */
-# include <strings.h>
-/* memory.h and strings.h conflict on some systems. */
-#endif /* not STDC_HEADERS and not HAVE_STRING_H */
#ifdef HAVE_LOCALE_H
# include <locale.h>