summaryrefslogtreecommitdiff
path: root/Src
diff options
context:
space:
mode:
Diffstat (limited to 'Src')
-rw-r--r--Src/Modules/curses.c104
-rw-r--r--Src/Modules/datetime.c8
-rw-r--r--Src/Modules/db_gdbm.c585
-rw-r--r--Src/Modules/db_gdbm.mdd2
-rw-r--r--Src/Modules/example.c3
-rw-r--r--Src/Modules/parameter.c132
-rw-r--r--Src/Modules/pcre.c37
-rw-r--r--Src/Modules/regex.c8
-rw-r--r--Src/Modules/system.c30
-rw-r--r--Src/Modules/tcp.c3
-rw-r--r--Src/Modules/zftp.c2
-rw-r--r--Src/Modules/zpty.c16
-rw-r--r--Src/Modules/zutil.c20
-rw-r--r--Src/Zle/compcore.c4
-rw-r--r--Src/Zle/compctl.c34
-rw-r--r--Src/Zle/complete.c4
-rw-r--r--Src/Zle/complist.c15
-rw-r--r--Src/Zle/compmatch.c57
-rw-r--r--Src/Zle/computil.c356
-rw-r--r--Src/Zle/textobjects.c9
-rw-r--r--Src/Zle/zle.h7
-rw-r--r--Src/Zle/zle_hist.c17
-rw-r--r--Src/Zle/zle_keymap.c2
-rw-r--r--Src/Zle/zle_main.c8
-rw-r--r--Src/Zle/zle_params.c11
-rw-r--r--Src/Zle/zle_refresh.c12
-rw-r--r--Src/Zle/zle_thingy.c27
-rw-r--r--Src/Zle/zle_tricky.c47
-rw-r--r--Src/builtin.c357
-rw-r--r--Src/compat.c322
-rw-r--r--Src/cond.c17
-rw-r--r--Src/exec.c349
-rw-r--r--Src/glob.c55
-rw-r--r--Src/hashtable.c185
-rw-r--r--Src/hist.c5
-rw-r--r--Src/init.c6
-rw-r--r--Src/input.c35
-rw-r--r--Src/jobs.c12
-rw-r--r--Src/lex.c19
-rw-r--r--Src/linklist.c29
-rw-r--r--Src/loop.c6
-rw-r--r--Src/math.c39
-rw-r--r--Src/module.c12
-rw-r--r--Src/options.c4
-rw-r--r--Src/params.c329
-rw-r--r--Src/parse.c115
-rw-r--r--Src/pattern.c12
-rw-r--r--Src/prompt.c3
-rw-r--r--Src/signals.c22
-rw-r--r--Src/string.c3
-rw-r--r--Src/subst.c93
-rw-r--r--Src/utils.c77
-rw-r--r--Src/watch.c107
-rw-r--r--Src/wcwidth9.h6
-rw-r--r--Src/zsh.h44
-rw-r--r--Src/ztype.h6
56 files changed, 2601 insertions, 1228 deletions
diff --git a/Src/Modules/curses.c b/Src/Modules/curses.c
index 63c6748f5..a60dfcbf8 100644
--- a/Src/Modules/curses.c
+++ b/Src/Modules/curses.c
@@ -1082,15 +1082,7 @@ zccmd_input(const char *nam, char **args)
#endif
/*
- * Some documentation for wgetch() says:
-
- The behavior of getch and friends in the presence of handled signals
- is unspecified in the SVr4 and XSI Curses documentation. Under his-
- torical curses implementations, it varied depending on whether the
- operating system's implementation of handled signal receipt interrupts
- a read(2) call in progress or not, and also (in some implementations)
- depending on whether an input timeout or non-blocking mode has been
- set.
+ * Linux, OS X, FreeBSD documentation for wgetch() mentions:
Programmers concerned about portability should be prepared for either
of two cases: (a) signal receipt does not interrupt getch; (b) signal
@@ -1098,21 +1090,16 @@ zccmd_input(const char *nam, char **args)
EINTR. Under the ncurses implementation, handled signals never inter-
rupt getch.
- * The observed behavior, however, is different: wgetch() consistently
- * returns ERR with EINTR when a signal is handled by the shell "trap"
- * command mechanism. Further, it consistently returns ERR twice, the
- * second time without even attempting to repeat the interrupted read,
- * which has the side-effect of NOT updating errno. A third call will
- * then begin reading again.
- *
- * Therefore, to properly implement signal trapping, we must (1) call
- * wgetch() in a loop as long as errno remains EINTR, and (2) clear
- * errno only before beginning the loop, not on every pass.
+ * Some observed behavior: wgetch() returns ERR with EINTR when a signal is
+ * handled by the shell "trap" command mechanism. Observed that it returns
+ * ERR twice, the second time without even attempting to repeat the
+ * interrupted read. Third call will then begin reading again.
*
- * There remains a potential bug here in that, if the caller has set
- * a timeout for the read [see zccmd_timeout()] the countdown is very
- * likely restarted on every call to wgetch(), so an interrupted call
- * might wait much longer than desired.
+ * Because of widespread of previous implementation that called wget*ch
+ * possibly indefinitely many times after ERR/EINTR, and because of the
+ * above observation, wget_wch call is repeated after each ERR/EINTR, but
+ * errno is being reset (it wasn't) and the loop to all means should break.
+ * Problem: the timeout may be waited twice.
*/
errno = 0;
@@ -1120,6 +1107,7 @@ zccmd_input(const char *nam, char **args)
while ((ret = wget_wch(w->win, &wi)) == ERR) {
if (errno != EINTR || errflag || retflag || breaks || exit_pending)
break;
+ errno = 0;
}
switch (ret) {
case OK:
@@ -1146,6 +1134,7 @@ zccmd_input(const char *nam, char **args)
while ((ci = wgetch(w->win)) == ERR) {
if (errno != EINTR || errflag || retflag || breaks || exit_pending)
return 1;
+ errno = 0;
}
if (ci >= 256) {
keypadnum = ci;
@@ -1501,6 +1490,74 @@ zccmd_touch(const char *nam, char **args)
return ret;
}
+static int
+zccmd_resize(const char *nam, char **args)
+{
+#ifdef HAVE_RESIZE_TERM
+ int y, x, do_endwin=0, do_save=1;
+ LinkNode stdscr_win = zcurses_getwindowbyname("stdscr");
+
+ if (stdscr_win) {
+ y = atoi(args[0]);
+ x = atoi(args[1]);
+ if (args[2]) {
+ if (0 == strcmp(args[2], "endwin")) {
+ do_endwin=1;
+ } else if (0 == strcmp(args[2], "endwin_nosave")) {
+ do_endwin=1;
+ do_save=0;
+ } else if (0 == strcmp(args[2], "nosave")) {
+ do_save=0;
+ } else {
+ zwarnnam(nam, "`resize' expects `endwin', `nosave' or `endwin_nosave' for third argument, if given");
+ }
+ }
+
+ if (y == 0 && x == 0 && args[2] == NULL) {
+ // Special case to just test that curses has resize_term. #ifdef
+ // HAVE_RESIZE_TERM will result in return value 2 if resize_term
+ // is not available.
+ return 0;
+ } else {
+ // Without this call some window moves are innacurate. Tested on
+ // OS X ncurses 5.4, Homebrew ncursesw 6.0-2, Arch Linux ncursesw
+ // 6.0, Ubuntu 14.04 ncurses 5.9, FreeBSD ncursesw.so.8
+ //
+ // On the other hand, the whole resize goal can be (from tests)
+ // accomplished by calling endwin and refresh. But to secure any
+ // future problems, resize_term is provided, and it is featured
+ // with endwin, so that users have multiple options.
+ if (do_endwin) {
+ endwin();
+ }
+
+ if( resize_term( y, x ) == OK ) {
+ // Things work without this, but we need to get out from
+ // endwin (i.e. call refresh), and in theory store new
+ // curses state (the resize might have changed it), which
+ // should be presented to terminal only after refresh.
+ if (do_endwin || do_save) {
+ ZCWin w;
+ w = (ZCWin)getdata(stdscr_win);
+ wnoutrefresh(w->win);
+ doupdate();
+ }
+
+ if (do_save) {
+ gettyinfo(&curses_tty_state);
+ }
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+ } else {
+ return 1;
+ }
+#else
+ return 2;
+#endif
+}
/*********************
Main builtin handler
@@ -1534,6 +1591,7 @@ bin_zcurses(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
{"mouse", zccmd_mouse, 0, -1},
{"querychar", zccmd_querychar, 1, 2},
{"touch", zccmd_touch, 1, -1},
+ {"resize", zccmd_resize, 2, 3},
{NULL, (zccmd_t)0, 0, 0}
};
diff --git a/Src/Modules/datetime.c b/Src/Modules/datetime.c
index bb82c542f..6e9047bc5 100644
--- a/Src/Modules/datetime.c
+++ b/Src/Modules/datetime.c
@@ -133,11 +133,15 @@ output_strftime(char *nam, char **argv, Options ops, UNUSED(int func))
len = 0;
for (x=0; x < 4; x++) {
- if ((len = ztrftime(buffer, bufsize, argv[0], t, 0L)) >= 0)
+ if ((len = ztrftime(buffer, bufsize, argv[0], t, 0L)) >= 0 || x==3)
break;
buffer = zrealloc(buffer, bufsize *= 2);
}
- DPUTS(len < 0, "bad output from ztrftime");
+ if (len < 0) {
+ zwarnnam(nam, "bad/unsupported format: '%s'", argv[0]);
+ zfree(buffer, bufsize);
+ return 1;
+ }
if (scalar) {
setsparam(scalar, metafy(buffer, len, META_DUP));
diff --git a/Src/Modules/db_gdbm.c b/Src/Modules/db_gdbm.c
index 8dd60fc0d..cf1322459 100644
--- a/Src/Modules/db_gdbm.c
+++ b/Src/Modules/db_gdbm.c
@@ -6,6 +6,9 @@
* Copyright (c) 2008 Clint Adams
* All rights reserved.
*
+ * Modifications copyright (c) 2017 Sebastian Gniazdowski
+ * All rights reserved.
+ *
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and to distribute modified versions of this software for any
@@ -31,6 +34,18 @@
#include "db_gdbm.mdh"
#include "db_gdbm.pro"
+#ifndef PM_UPTODATE
+#define PM_UPTODATE (1<<19) /* Parameter has up-to-date data (e.g. loaded from DB) */
+#endif
+
+static Param createhash( char *name, int flags );
+static int append_tied_name( const char *name );
+static int remove_tied_name( const char *name );
+static char *unmetafy_zalloc(const char *to_copy, int *new_len);
+static void myfreeparamnode(HashNode hn);
+
+static int no_database_action = 0;
+
/*
* Make sure we have all the bits I'm using for memory mapping, otherwise
* I don't know what I'm doing.
@@ -41,8 +56,34 @@
static char *backtype = "db/gdbm";
-static const struct gsu_scalar gdbm_gsu =
-{ gdbmgetfn, gdbmsetfn, gdbmunsetfn };
+/*
+ * Longer GSU structure, to carry GDBM_FILE of owning
+ * database. Every parameter (hash value) receives GSU
+ * pointer and thus also receives GDBM_FILE - this way
+ * parameters can access proper database.
+ *
+ * Main HashTable parameter has the same instance of
+ * the custom GSU struct in u.hash->tmpdata field.
+ * When database is closed, `dbf` field is set to NULL
+ * and hash values know to not access database when
+ * being unset (total purge at zuntie).
+ *
+ * When database closing is ended, custom GSU struct
+ * is freed. Only new ztie creates new custom GSU
+ * struct instance.
+ */
+
+struct gsu_scalar_ext {
+ struct gsu_scalar std; /* Size of three pointers */
+ GDBM_FILE dbf;
+ char *dbfile_path;
+};
+
+/* Source structure - will be copied to allocated one,
+ * with `dbf` filled. `dbf` allocation <-> gsu allocation. */
+static const struct gsu_scalar_ext gdbm_gsu_ext =
+{ { gdbmgetfn, gdbmsetfn, gdbmunsetfn }, 0, 0 };
+
/**/
static const struct gsu_hash gdbm_hash_gsu =
{ hashgetfn, gdbmhashsetfn, gdbmhashunsetfn };
@@ -50,12 +91,24 @@ static const struct gsu_hash gdbm_hash_gsu =
static struct builtin bintab[] = {
BUILTIN("ztie", 0, bin_ztie, 1, -1, 0, "d:f:r", NULL),
BUILTIN("zuntie", 0, bin_zuntie, 1, -1, 0, "u", NULL),
+ BUILTIN("zgdbmpath", 0, bin_zgdbmpath, 1, -1, 0, "", NULL),
+};
+
+#define ROARRPARAMDEF(name, var) \
+ { name, PM_ARRAY | PM_READONLY, (void *) var, NULL, NULL, NULL, NULL }
+
+/* Holds names of all tied parameters */
+char **zgdbm_tied;
+
+static struct paramdef patab[] = {
+ ROARRPARAMDEF( "zgdbm_tied", &zgdbm_tied ),
};
/**/
static int
bin_ztie(char *nam, char **args, Options ops, UNUSED(int func))
{
+ struct gsu_scalar_ext *dbf_carrier;
char *resource_name, *pmname;
GDBM_FILE dbf = NULL;
int read_write = GDBM_SYNC, pmflags = PM_REMOVABLE;
@@ -77,8 +130,7 @@ bin_ztie(char *nam, char **args, Options ops, UNUSED(int func))
}
/* Here should be a lookup of the backend type against
- * a registry.
- */
+ * a registry, if generam DB mechanism is to be added */
if (strcmp(OPT_ARG(ops, 'd'), backtype) != 0) {
zwarnnam(nam, "unsupported backend type `%s'", OPT_ARG(ops, 'd'));
return 1;
@@ -92,7 +144,8 @@ bin_ztie(char *nam, char **args, Options ops, UNUSED(int func))
/*
* Unset any existing parameter. Note there's no implicit
* "local" here, but if the existing parameter is local
- * that will be reflected in the new one.
+ * then new parameter will be also local without following
+ * unset.
*
* We need to do this before attempting to open the DB
* in case this variable is already tied to a DB.
@@ -105,24 +158,40 @@ bin_ztie(char *nam, char **args, Options ops, UNUSED(int func))
return 1;
}
+ gdbm_errno=0;
dbf = gdbm_open(resource_name, 0, read_write, 0666, 0);
- if(dbf)
- addmodulefd(gdbm_fdesc(dbf), FDT_INTERNAL);
- else {
- zwarnnam(nam, "error opening database file %s", resource_name);
+ if(dbf == NULL) {
+ zwarnnam(nam, "error opening database file %s (%s)", resource_name, gdbm_strerror(gdbm_errno));
return 1;
}
- if (!(tied_param = createspecialhash(pmname, &getgdbmnode, &scangdbmkeys,
- pmflags))) {
+ if (!(tied_param = createhash(pmname, pmflags))) {
zwarnnam(nam, "cannot create the requested parameter %s", pmname);
- fdtable[gdbm_fdesc(dbf)] = FDT_UNUSED;
gdbm_close(dbf);
return 1;
}
tied_param->gsu.h = &gdbm_hash_gsu;
- tied_param->u.hash->tmpdata = (void *)dbf;
+
+ /* Allocate parameter sub-gsu, fill dbf field.
+ * dbf allocation is 1 to 1 accompanied by
+ * gsu_scalar_ext allocation. */
+
+ dbf_carrier = (struct gsu_scalar_ext *) zalloc(sizeof(struct gsu_scalar_ext));
+ dbf_carrier->std = gdbm_gsu_ext.std;
+ dbf_carrier->dbf = dbf;
+ tied_param->u.hash->tmpdata = (void *)dbf_carrier;
+
+ /* Fill also file path field */
+ if (*resource_name != '/') {
+ /* Code copied from check_autoload() */
+ resource_name = zhtricat(metafy(zgetcwd(), -1, META_HEAPDUP), "/", resource_name);
+ resource_name = xsymlink(resource_name, 1);
+ }
+ dbf_carrier->dbfile_path = ztrdup(resource_name);
+
+ addmodulefd(gdbm_fdesc(dbf), FDT_INTERNAL);
+ append_tied_name(pmname);
return 0;
}
@@ -149,8 +218,9 @@ bin_zuntie(char *nam, char **args, Options ops, UNUSED(int func))
}
queue_signals();
- if (OPT_ISSET(ops,'u'))
- gdbmuntie(pm); /* clear read-only-ness */
+ if (OPT_ISSET(ops,'u')) {
+ pm->node.flags &= ~PM_READONLY;
+ }
if (unsetparam_pm(pm, 0, 1)) {
/* assume already reported */
ret = 1;
@@ -162,25 +232,112 @@ bin_zuntie(char *nam, char **args, Options ops, UNUSED(int func))
}
/**/
+static int
+bin_zgdbmpath(char *nam, char **args, Options ops, UNUSED(int func))
+{
+ Param pm;
+ char *pmname;
+
+ pmname = *args;
+
+ if (!pmname) {
+ zwarnnam(nam, "parameter name (whose path is to be written to $REPLY) is required");
+ return 1;
+ }
+
+ pm = (Param) paramtab->getnode(paramtab, pmname);
+ if(!pm) {
+ zwarnnam(nam, "no such parameter: %s", pmname);
+ return 1;
+ }
+
+ if (pm->gsu.h != &gdbm_hash_gsu) {
+ zwarnnam(nam, "not a tied gdbm parameter: %s", pmname);
+ return 1;
+ }
+
+ /* Paranoia, it *will* be always set */
+ if (((struct gsu_scalar_ext *)pm->u.hash->tmpdata)->dbfile_path) {
+ setsparam("REPLY", ztrdup(((struct gsu_scalar_ext *)pm->u.hash->tmpdata)->dbfile_path));
+ } else {
+ setsparam("REPLY", ztrdup(""));
+ }
+
+ return 0;
+}
+
+/*
+ * The param is actual param in hash – always, because
+ * getgdbmnode creates every new key seen. However, it
+ * might be not PM_UPTODATE - which means that database
+ * wasn't yet queried.
+ *
+ * It will be left in this state if database doesn't
+ * contain such key. That might be a drawback, maybe
+ * setting to empty value has sense.
+ */
+
+/**/
static char *
gdbmgetfn(Param pm)
{
datum key, content;
- int ret;
+ int ret, umlen;
+ char *umkey;
GDBM_FILE dbf;
- key.dptr = pm->node.nam;
- key.dsize = strlen(key.dptr) + 1;
+ /* Key already retrieved? There is no sense of asking the
+ * database again, because:
+ * - there can be only multiple readers
+ * - so, no writer + reader use is allowed
+ *
+ * Thus:
+ * - if we are writers, we for sure have newest copy of data
+ * - if we are readers, we for sure have newest copy of data
+ */
+ if ( pm->node.flags & PM_UPTODATE ) {
+ return pm->u.str ? pm->u.str : "";
+ }
+
+ /* Unmetafy key. GDBM fits nice into this
+ * process, as it uses length of data */
+ umlen = 0;
+ umkey = unmetafy_zalloc(pm->node.nam,&umlen);
+
+ key.dptr = umkey;
+ key.dsize = umlen;
+
+ dbf = ((struct gsu_scalar_ext *)pm->gsu.s)->dbf;
+
+ if((ret = gdbm_exists(dbf, key))) {
+ /* We have data – store it, return it */
+ pm->node.flags |= PM_UPTODATE;
- dbf = (GDBM_FILE)(pm->u.hash->tmpdata);
- ret = gdbm_exists(dbf, key);
- if(ret) {
content = gdbm_fetch(dbf, key);
- } else {
- content.dptr = dupstring("");
+
+ /* Ensure there's no leak */
+ if (pm->u.str) {
+ zsfree(pm->u.str);
+ pm->u.str = NULL;
+ }
+
+ /* Metafy returned data. All fits - metafy
+ * can obtain data length to avoid using \0 */
+ pm->u.str = metafy(content.dptr, content.dsize, META_DUP);
+ /* gdbm allocates with malloc */
+ free(content.dptr);
+
+ /* Free key */
+ zfree(umkey, umlen+1);
+
+ /* Can return pointer, correctly saved inside hash */
+ return pm->u.str;
}
- return content.dptr;
+ /* Free key */
+ zfree(umkey, umlen+1);
+
+ return "";
}
/**/
@@ -190,78 +347,128 @@ gdbmsetfn(Param pm, char *val)
datum key, content;
GDBM_FILE dbf;
- key.dptr = pm->node.nam;
- key.dsize = strlen(key.dptr) + 1;
- content.dptr = val;
- content.dsize = strlen(content.dptr) + 1;
+ /* Set is done on parameter and on database.
+ * See the allowed workers / readers comment
+ * at gdbmgetfn() */
- dbf = (GDBM_FILE)(pm->u.hash->tmpdata);
- (void)gdbm_store(dbf, key, content, GDBM_REPLACE);
+ /* Parameter */
+ if (pm->u.str) {
+ zsfree(pm->u.str);
+ pm->u.str = NULL;
+ pm->node.flags &= ~(PM_UPTODATE);
+ }
+
+ if (val) {
+ pm->u.str = ztrdup(val);
+ pm->node.flags |= PM_UPTODATE;
+ }
+
+ /* Database */
+ dbf = ((struct gsu_scalar_ext *)pm->gsu.s)->dbf;
+ if (dbf && no_database_action == 0) {
+ int umlen = 0;
+ char *umkey = unmetafy_zalloc(pm->node.nam,&umlen);
+
+ key.dptr = umkey;
+ key.dsize = umlen;
+
+ if (val) {
+ /* Unmetafy with exact zalloc size */
+ char *umval = unmetafy_zalloc(val,&umlen);
+
+ /* Store */
+ content.dptr = umval;
+ content.dsize = umlen;
+ (void)gdbm_store(dbf, key, content, GDBM_REPLACE);
+
+ /* Free */
+ zfree(umval, umlen+1);
+ } else {
+ (void)gdbm_delete(dbf, key);
+ }
+
+ /* Free key */
+ zfree(umkey, key.dsize+1);
+ }
}
/**/
static void
gdbmunsetfn(Param pm, UNUSED(int um))
{
- datum key;
- GDBM_FILE dbf;
-
- key.dptr = pm->node.nam;
- key.dsize = strlen(key.dptr) + 1;
-
- dbf = (GDBM_FILE)(pm->u.hash->tmpdata);
- (void)gdbm_delete(dbf, key);
+ /* Set with NULL */
+ gdbmsetfn(pm, NULL);
}
/**/
static HashNode
getgdbmnode(HashTable ht, const char *name)
{
- int len;
- char *nameu;
- Param pm = NULL;
-
- nameu = dupstring(name);
- unmetafy(nameu, &len);
-
- pm = (Param) hcalloc(sizeof(struct param));
- pm->node.nam = nameu;
- pm->node.flags = PM_SCALAR;
- pm->gsu.s = &gdbm_gsu;
- pm->u.hash = ht;
+ HashNode hn = gethashnode2( ht, name );
+ Param val_pm = (Param) hn;
+
+ /* Entry for key doesn't exist? Create it now,
+ * it will be interfacing between the database
+ * and Zsh - through special gdbm_gsu. So, any
+ * seen key results in new interfacing parameter.
+ *
+ * Previous code was returning heap arena Param
+ * that wasn't actually added to the hash. It was
+ * plainly name / database-key holder. Here we
+ * add the Param to its hash, it is not PM_UPTODATE.
+ * It will be loaded from database *and filled*
+ * or left in that state if the database doesn't
+ * contain it.
+ *
+ * No heap arena memory is used, memory usage is
+ * now limited - by number of distinct keys seen,
+ * not by number of key *uses*.
+ * */
+
+ if ( ! val_pm ) {
+ val_pm = (Param) zshcalloc( sizeof (*val_pm) );
+ val_pm->node.flags = PM_SCALAR | PM_HASHELEM; /* no PM_UPTODATE */
+ val_pm->gsu.s = (GsuScalar) ht->tmpdata;
+ ht->addnode( ht, ztrdup( name ), val_pm ); /* sets pm->node.nam */
+ }
- return &pm->node;
+ return (HashNode) val_pm;
}
/**/
static void
scangdbmkeys(HashTable ht, ScanFunc func, int flags)
{
- Param pm = NULL;
- datum key, content;
- GDBM_FILE dbf = (GDBM_FILE)(ht->tmpdata);
-
- pm = (Param) hcalloc(sizeof(struct param));
-
- pm->node.flags = PM_SCALAR;
- pm->gsu.s = &nullsetscalar_gsu;
+ datum key, prev_key;
+ GDBM_FILE dbf = ((struct gsu_scalar_ext *)ht->tmpdata)->dbf;
+ /* Iterate keys adding them to hash, so
+ * we have Param to use in `func` */
key = gdbm_firstkey(dbf);
while(key.dptr) {
- content = gdbm_fetch(dbf, key);
-
- pm->node.nam = key.dptr;
- pm->u.str = content.dptr;
- pm->gsu.s = &nullsetscalar_gsu;
-
- func(&pm->node, flags);
-
+ /* This returns database-interfacing Param,
+ * it will return u.str or first fetch data
+ * if not PM_UPTODATE (newly created) */
+ char *zkey = metafy(key.dptr, key.dsize, META_DUP);
+ HashNode hn = getgdbmnode(ht, zkey);
+ zsfree( zkey );
+
+ func(hn, flags);
+
+ /* Iterate - no problem as interfacing Param
+ * will do at most only fetches, not stores */
+ prev_key = key;
key = gdbm_nextkey(dbf, key);
+ free(prev_key.dptr);
}
}
+/*
+ * Replace database with new hash
+ */
+
/**/
static void
gdbmhashsetfn(Param pm, HashTable ht)
@@ -274,7 +481,7 @@ gdbmhashsetfn(Param pm, HashTable ht)
if (!pm->u.hash || pm->u.hash == ht)
return;
- if (!(dbf = (GDBM_FILE)(pm->u.hash->tmpdata)))
+ if (!(dbf = ((struct gsu_scalar_ext *)pm->u.hash->tmpdata)->dbf))
return;
key = gdbm_firstkey(dbf);
@@ -286,48 +493,78 @@ gdbmhashsetfn(Param pm, HashTable ht)
key = gdbm_firstkey(dbf);
}
- /* just deleted everything, clean up */
- (void)gdbm_reorganize(dbf);
+ /* Just deleted everything, clean up if no new data.
+ * User can also reorganize via gdbmtool. */
+ if (!ht || ht->hsize == 0) {
+ (void)gdbm_reorganize(dbf);
+ }
+
+ no_database_action = 1;
+ emptyhashtable(pm->u.hash);
+ no_database_action = 0;
if (!ht)
return;
- for (i = 0; i < ht->hsize; i++)
+ /* Put new strings into database, waiting
+ * for their interfacing-Params to be created */
+
+ for (i = 0; i < ht->hsize; i++) {
for (hn = ht->nodes[i]; hn; hn = hn->next) {
struct value v;
+ int umlen = 0;
+ char *umkey, *umval;
v.isarr = v.flags = v.start = 0;
v.end = -1;
v.arr = NULL;
v.pm = (Param) hn;
- key.dptr = v.pm->node.nam;
- key.dsize = strlen(key.dptr) + 1;
+ /* Unmetafy key */
+ umkey = unmetafy_zalloc(v.pm->node.nam,&umlen);
+
+ key.dptr = umkey;
+ key.dsize = umlen;
queue_signals();
- content.dptr = getstrvalue(&v);
- content.dsize = strlen(content.dptr) + 1;
+ /* Unmetafy */
+ umval = unmetafy_zalloc(getstrvalue(&v),&umlen);
+ /* Store */
+ content.dptr = umval;
+ content.dsize = umlen;
(void)gdbm_store(dbf, key, content, GDBM_REPLACE);
+ /* Free - unmetafy_zalloc allocates
+ * exact required space + 1 null byte */
+ zfree(umval, content.dsize+1);
+ zfree(umkey, key.dsize+1);
+
unqueue_signals();
}
+ }
+ /* We reuse our hash, the input is to be deleted */
+ deleteparamtable(ht);
}
/**/
static void
gdbmuntie(Param pm)
{
- GDBM_FILE dbf = (GDBM_FILE)(pm->u.hash->tmpdata);
+ GDBM_FILE dbf = ((struct gsu_scalar_ext *)pm->u.hash->tmpdata)->dbf;
HashTable ht = pm->u.hash;
if (dbf) { /* paranoia */
fdtable[gdbm_fdesc(dbf)] = FDT_UNUSED;
- gdbm_close(dbf);
- }
+ gdbm_close(dbf);
+
+ /* Let hash fields know there's no backend */
+ ((struct gsu_scalar_ext *)ht->tmpdata)->dbf = NULL;
- ht->tmpdata = NULL;
+ /* Remove from list of tied parameters */
+ remove_tied_name(pm->node.nam);
+ }
/* for completeness ... createspecialhash() should have an inverse */
ht->getnode = ht->getnode2 = gethashnode2;
@@ -341,21 +578,31 @@ gdbmuntie(Param pm)
static void
gdbmhashunsetfn(Param pm, UNUSED(int exp))
{
+ struct gsu_scalar_ext * gsu_ext;
+
gdbmuntie(pm);
- /* hash table is now normal, so proceed normally... */
+
+ /* Remember custom GSU structure assigned to
+ * u.hash->tmpdata before hash gets deleted */
+ gsu_ext = pm->u.hash->tmpdata;
+
+ /* Uses normal unsetter (because gdbmuntie is called above).
+ * Will delete all owned field-parameters and also hashtable. */
pm->gsu.h->setfn(pm, NULL);
+
+ /* Don't need custom GSU structure with its
+ * GDBM_FILE pointer anymore */
+ zsfree( gsu_ext->dbfile_path );
+ zfree( gsu_ext, sizeof(struct gsu_scalar_ext));
+
pm->node.flags |= PM_UNSET;
}
-#else
-# error no gdbm
-#endif /* have gdbm */
-
static struct features module_features = {
bintab, sizeof(bintab)/sizeof(*bintab),
NULL, 0,
NULL, 0,
- NULL, 0,
+ patab, sizeof(patab)/sizeof(*patab),
0
};
@@ -385,6 +632,7 @@ enables_(Module m, int **enables)
int
boot_(UNUSED(Module m))
{
+ zgdbm_tied = zshcalloc((1) * sizeof(char *));
return 0;
}
@@ -392,6 +640,7 @@ boot_(UNUSED(Module m))
int
cleanup_(Module m)
{
+ /* This frees `zgdbm_tied` */
return setfeatureenables(m, &module_features, NULL);
}
@@ -401,3 +650,169 @@ finish_(UNUSED(Module m))
{
return 0;
}
+
+/*********************
+ * Utility functions *
+ *********************/
+
+static Param createhash( char *name, int flags ) {
+ Param pm;
+ HashTable ht;
+
+ pm = createparam(name, flags | PM_SPECIAL | PM_HASHED);
+ if (!pm) {
+ return NULL;
+ }
+
+ if (pm->old)
+ pm->level = locallevel;
+
+ /* This creates standard hash. */
+ ht = pm->u.hash = newparamtable(17, name);
+ if (!pm->u.hash) {
+ paramtab->removenode(paramtab, name);
+ paramtab->freenode(&pm->node);
+ zwarnnam(name, "out of memory when allocating hash");
+ return NULL;
+ }
+
+ /* Does free Param (unsetfn is called) */
+ ht->freenode = myfreeparamnode;
+
+ /* These provide special features */
+ ht->getnode = ht->getnode2 = getgdbmnode;
+ ht->scantab = scangdbmkeys;
+
+ return pm;
+}
+
+/*
+ * Adds parameter name to `zgdbm_tied`
+ */
+
+static int append_tied_name( const char *name ) {
+ int old_len = arrlen(zgdbm_tied);
+ char **new_zgdbm_tied = zshcalloc( (old_len+2) * sizeof(char *));
+
+ /* Copy */
+ char **p = zgdbm_tied;
+ char **dst = new_zgdbm_tied;
+ while (*p) {
+ *dst++ = *p++;
+ }
+
+ /* Append new one */
+ *dst = ztrdup(name);
+
+ /* Substitute, free old one */
+ zfree(zgdbm_tied, sizeof(char *) * (old_len + 1));
+ zgdbm_tied = new_zgdbm_tied;
+
+ return 0;
+}
+
+/*
+ * Removes parameter name from `zgdbm_tied`
+ */
+
+static int remove_tied_name( const char *name ) {
+ int old_len = arrlen(zgdbm_tied);
+ int new_len;
+
+ /* Two stage, to always have arrlen() == zfree-size - 1.
+ * Could do allocation and revert when `not found`, but
+ * what would be better about that. */
+
+ /* Find one to remove */
+ char **p = zgdbm_tied;
+ while (*p) {
+ if (0==strcmp(name,*p)) {
+ break;
+ }
+ p++;
+ }
+
+ /* Copy x+1 to x */
+ while (*p) {
+ *p=*(p+1);
+ p++;
+ }
+
+ /* Second stage. Size changed? Only old_size-1
+ * change is possible, but.. paranoia way */
+ new_len = arrlen(zgdbm_tied);
+ if (new_len != old_len) {
+ char **dst;
+ char **new_zgdbm_tied = zshcalloc((new_len+1) * sizeof(char *));
+
+ /* Copy */
+ p = zgdbm_tied;
+ dst = new_zgdbm_tied;
+ while (*p) {
+ *dst++ = *p++;
+ }
+ *dst = NULL;
+
+ /* Substitute, free old one */
+ zfree(zgdbm_tied, sizeof(char *) * (old_len + 1));
+ zgdbm_tied = new_zgdbm_tied;
+ }
+
+ return 0;
+}
+
+/*
+ * Unmetafy that:
+ * - duplicates bufer to work on it,
+ * - does zalloc of exact size for the new string,
+ * - restores work buffer to original content, to restore strlen
+ */
+static char *
+unmetafy_zalloc(const char *to_copy, int *new_len) {
+ char *work, *to_return;
+ int my_new_len = 0;
+
+ work = ztrdup(to_copy);
+ work = unmetafy(work,&my_new_len);
+
+ if (new_len)
+ *new_len = my_new_len;
+
+ /* This string can be correctly zsfree()-d */
+ to_return = (char *) zalloc((my_new_len+1)*sizeof(char));
+ memcpy(to_return, work, sizeof(char)*my_new_len); /* memcpy handles $'\0' */
+ to_return[my_new_len]='\0';
+
+ /* Restore original strlen and correctly free */
+ strcpy(work, to_copy);
+ zsfree(work);
+
+ return to_return;
+}
+
+static void
+myfreeparamnode(HashNode hn)
+{
+ Param pm = (Param) hn;
+
+ /* Upstream: The second argument of unsetfn() is used by modules to
+ * differentiate "exp"licit unset from implicit unset, as when
+ * a parameter is going out of scope. It's not clear which
+ * of these applies here, but passing 1 has always worked.
+ */
+
+ /* if (delunset) */
+ pm->gsu.s->unsetfn(pm, 1);
+
+ zsfree(pm->node.nam);
+ /* If this variable was tied by the user, ename was ztrdup'd */
+ if (pm->node.flags & PM_TIED && pm->ename) {
+ zsfree(pm->ename);
+ pm->ename = NULL;
+ }
+ zfree(pm, sizeof(struct param));
+}
+
+#else
+# error no gdbm
+#endif /* have gdbm */
diff --git a/Src/Modules/db_gdbm.mdd b/Src/Modules/db_gdbm.mdd
index ce7926bd9..210c22177 100644
--- a/Src/Modules/db_gdbm.mdd
+++ b/Src/Modules/db_gdbm.mdd
@@ -7,6 +7,6 @@ fi
'
load=no
-autofeatures="b:ztie b:zuntie"
+autofeatures="b:ztie b:zuntie b:zgdbmpath p:zgdbm_tied"
objects="db_gdbm.o"
diff --git a/Src/Modules/example.c b/Src/Modules/example.c
index 45ca2cffa..c80c9e7b2 100644
--- a/Src/Modules/example.c
+++ b/Src/Modules/example.c
@@ -69,7 +69,8 @@ bin_example(char *nam, char **args, Options ops, UNUSED(int func))
intparam = i;
zsfree(strparam);
strparam = ztrdup(*oargs ? *oargs : "");
- freearray(arrparam);
+ if (arrparam)
+ freearray(arrparam);
arrparam = zarrdup(oargs);
return 0;
}
diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c
index 98bcaba6e..10c47d214 100644
--- a/Src/Modules/parameter.c
+++ b/Src/Modules/parameter.c
@@ -167,7 +167,7 @@ unsetpmcommand(Param pm, UNUSED(int exp))
/**/
static void
-setpmcommands(UNUSED(Param pm), HashTable ht)
+setpmcommands(Param pm, HashTable ht)
{
int i;
HashNode hn;
@@ -190,7 +190,15 @@ setpmcommands(UNUSED(Param pm), HashTable ht)
cmdnamtab->addnode(cmdnamtab, ztrdup(hn->nam), &cn->node);
}
- deleteparamtable(ht);
+ /*
+ * On full-array assignment ht is a temporary hash with the default
+ * get/set functions, whereas pm->u.hash has the special $commands
+ * get/set functions. Do not assign ht to pm, just delete it.
+ *
+ * On append, ht and pm->u.hash are the same table, don't delete.
+ */
+ if (ht != pm->u.hash)
+ deleteparamtable(ht);
}
static const struct gsu_scalar pmcommand_gsu =
@@ -330,7 +338,7 @@ unsetpmfunction(Param pm, UNUSED(int exp))
/**/
static void
-setfunctions(UNUSED(Param pm), HashTable ht, int dis)
+setfunctions(Param pm, HashTable ht, int dis)
{
int i;
HashNode hn;
@@ -349,7 +357,9 @@ setfunctions(UNUSED(Param pm), HashTable ht, int dis)
setfunction(hn->nam, ztrdup(getstrvalue(&v)), dis);
}
- deleteparamtable(ht);
+ /* See setpmcommands() above */
+ if (ht != pm->u.hash)
+ deleteparamtable(ht);
}
/**/
@@ -515,6 +525,98 @@ scanpmdisfunctions(HashTable ht, ScanFunc func, int flags)
scanfunctions(ht, func, flags, DISABLED);
}
+/* Functions for the functions_source special parameter. */
+
+/* Retrieve the source file for a function by explicit name */
+
+/**/
+static HashNode
+getfunction_source(UNUSED(HashTable ht), const char *name, int dis)
+{
+ Shfunc shf;
+ Param pm = NULL;
+
+ pm = (Param) hcalloc(sizeof(struct param));
+ pm->node.nam = dupstring(name);
+ pm->node.flags = PM_SCALAR|PM_READONLY;
+ pm->gsu.s = dis ? &pmdisfunction_gsu : &pmfunction_gsu;
+
+ if ((shf = (Shfunc) shfunctab->getnode2(shfunctab, name)) &&
+ (dis ? (shf->node.flags & DISABLED) : !(shf->node.flags & DISABLED))) {
+ pm->u.str = getshfuncfile(shf);
+ if (!pm->u.str)
+ pm->u.str = dupstring("");
+ }
+ return &pm->node;
+}
+
+/* Retrieve the source file for functions by scanning the table */
+
+/**/
+static void
+scanfunctions_source(UNUSED(HashTable ht), ScanFunc func, int flags, int dis)
+{
+ struct param pm;
+ int i;
+ HashNode hn;
+
+ memset((void *)&pm, 0, sizeof(struct param));
+ pm.node.flags = PM_SCALAR|PM_READONLY;
+ pm.gsu.s = dis ? &pmdisfunction_gsu : &pmfunction_gsu;
+
+ for (i = 0; i < shfunctab->hsize; i++) {
+ for (hn = shfunctab->nodes[i]; hn; hn = hn->next) {
+ if (dis ? (hn->flags & DISABLED) : !(hn->flags & DISABLED)) {
+ pm.node.nam = hn->nam;
+ if (func != scancountparams &&
+ ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
+ !(flags & SCANPM_WANTKEYS))) {
+ pm.u.str = getshfuncfile((Shfunc)hn);
+ if (!pm.u.str)
+ pm.u.str = dupstring("");
+ }
+ func(&pm.node, flags);
+ }
+ }
+ }
+}
+
+/* Param table entry for retrieving functions_source element */
+
+/**/
+static HashNode
+getpmfunction_source(HashTable ht, const char *name)
+{
+ return getfunction_source(ht, name, 0);
+}
+
+/* Param table entry for retrieving ds_functions_source element */
+
+/**/
+static HashNode
+getpmdisfunction_source(HashTable ht, const char *name)
+{
+ return getfunction_source(ht, name, 1);
+}
+
+/* Param table entry for scanning functions_source table */
+
+/**/
+static void
+scanpmfunction_source(HashTable ht, ScanFunc func, int flags)
+{
+ scanfunctions_source(ht, func, flags, 0);
+}
+
+/* Param table entry for scanning dis_functions_source table */
+
+/**/
+static void
+scanpmdisfunction_source(HashTable ht, ScanFunc func, int flags)
+{
+ scanfunctions_source(ht, func, flags, 1);
+}
+
/* Functions for the funcstack special parameter. */
/**/
@@ -845,7 +947,7 @@ unsetpmoption(Param pm, UNUSED(int exp))
/**/
static void
-setpmoptions(UNUSED(Param pm), HashTable ht)
+setpmoptions(Param pm, HashTable ht)
{
int i;
HashNode hn;
@@ -870,7 +972,9 @@ setpmoptions(UNUSED(Param pm), HashTable ht)
(val && strcmp(val, "off")), 0, opts))
zwarn("can't change option: %s", hn->nam);
}
- deleteparamtable(ht);
+ /* See setpmcommands() above */
+ if (ht != pm->u.hash)
+ deleteparamtable(ht);
}
static const struct gsu_scalar pmoption_gsu =
@@ -1409,7 +1513,7 @@ unsetpmnameddir(Param pm, UNUSED(int exp))
/**/
static void
-setpmnameddirs(UNUSED(Param pm), HashTable ht)
+setpmnameddirs(Param pm, HashTable ht)
{
int i;
HashNode hn, next, hd;
@@ -1451,7 +1555,9 @@ setpmnameddirs(UNUSED(Param pm), HashTable ht)
i = opts[INTERACTIVE];
opts[INTERACTIVE] = 0;
- deleteparamtable(ht);
+ /* See setpmcommands() above */
+ if (ht != pm->u.hash)
+ deleteparamtable(ht);
opts[INTERACTIVE] = i;
}
@@ -1632,7 +1738,7 @@ unsetpmsalias(Param pm, UNUSED(int exp))
/**/
static void
-setaliases(HashTable alht, UNUSED(Param pm), HashTable ht, int flags)
+setaliases(HashTable alht, Param pm, HashTable ht, int flags)
{
int i;
HashNode hn, next, hd;
@@ -1668,7 +1774,9 @@ setaliases(HashTable alht, UNUSED(Param pm), HashTable ht, int flags)
alht->addnode(alht, ztrdup(hn->nam),
createaliasnode(ztrdup(val), flags));
}
- deleteparamtable(ht);
+ /* See setpmcommands() above */
+ if (ht != pm->u.hash)
+ deleteparamtable(ht);
}
/**/
@@ -2095,6 +2203,8 @@ static struct paramdef partab[] = {
NULL, getpmdisbuiltin, scanpmdisbuiltins),
SPECIALPMDEF("dis_functions", 0,
&pmdisfunctions_gsu, getpmdisfunction, scanpmdisfunctions),
+ SPECIALPMDEF("dis_functions_source", PM_READONLY, NULL,
+ getpmdisfunction_source, scanpmdisfunction_source),
SPECIALPMDEF("dis_galiases", 0,
&pmdisgaliases_gsu, getpmdisgalias, scanpmdisgaliases),
SPECIALPMDEF("dis_patchars", PM_ARRAY|PM_READONLY,
@@ -2111,6 +2221,8 @@ static struct paramdef partab[] = {
&funcstack_gsu, NULL, NULL),
SPECIALPMDEF("functions", 0, &pmfunctions_gsu, getpmfunction,
scanpmfunctions),
+ SPECIALPMDEF("functions_source", PM_READONLY, NULL,
+ getpmfunction_source, scanpmfunction_source),
SPECIALPMDEF("functrace", PM_ARRAY|PM_READONLY,
&functrace_gsu, NULL, NULL),
SPECIALPMDEF("galiases", 0,
diff --git a/Src/Modules/pcre.c b/Src/Modules/pcre.c
index 5fd67963d..27191d709 100644
--- a/Src/Modules/pcre.c
+++ b/Src/Modules/pcre.c
@@ -75,7 +75,7 @@ zpcre_utf8_enabled(void)
static int
bin_pcre_compile(char *nam, char **args, Options ops, UNUSED(int func))
{
- int pcre_opts = 0, pcre_errptr;
+ int pcre_opts = 0, pcre_errptr, target_len;
const char *pcre_error;
char *target;
@@ -89,15 +89,19 @@ bin_pcre_compile(char *nam, char **args, Options ops, UNUSED(int func))
pcre_opts |= PCRE_UTF8;
pcre_hints = NULL; /* Is this necessary? */
-
+
if (pcre_pattern)
pcre_free(pcre_pattern);
target = ztrdup(*args);
- unmetafy(target, NULL);
+ unmetafy(target, &target_len);
+
+ if ((int)strlen(target) != target_len) {
+ zwarnnam(nam, "embedded NULs in PCRE pattern terminate pattern");
+ }
pcre_pattern = pcre_compile(target, pcre_opts, &pcre_error, &pcre_errptr, NULL);
-
+
free(target);
if (pcre_pattern == NULL)
@@ -167,7 +171,12 @@ zpcre_get_substrings(char *arg, int *ovec, int ret, char *matchvar,
sprintf(offset_all, "%d %d", ovec[0], ovec[1]);
setsparam("ZPCRE_OP", ztrdup(offset_all));
}
- match_all = metafy(captures[0], -1, META_DUP);
+ /*
+ * Result strings can contain embedded NULs; the length of each is the
+ * difference between the two values in each paired entry in ovec.
+ * ovec is length 2*(1+capture_list_length)
+ */
+ match_all = metafy(captures[0], ovec[1] - ovec[0], META_DUP);
setsparam(matchvar, match_all);
/*
* If we're setting match, mbegin, mend we only do
@@ -176,13 +185,16 @@ zpcre_get_substrings(char *arg, int *ovec, int ret, char *matchvar,
*/
if (!want_begin_end || nelem) {
char **x, **y;
+ int vec_off;
y = &captures[capture_start];
matches = x = (char **) zalloc(sizeof(char *) * (arrlen(y) + 1));
+ vec_off = 2;
do {
if (*y)
- *x++ = metafy(*y, -1, META_DUP);
+ *x++ = metafy(*y, ovec[vec_off+1]-ovec[vec_off], META_DUP);
else
*x++ = NULL;
+ vec_off += 2;
} while (*y++);
setaparam(substravar, matches);
}
@@ -318,8 +330,7 @@ bin_pcre_match(char *nam, char **args, Options ops, UNUSED(int func))
ovec = zalloc(ovecsize*sizeof(int));
plaintext = ztrdup(*args);
- unmetafy(plaintext, NULL);
- subject_len = (int)strlen(plaintext);
+ unmetafy(plaintext, &subject_len);
if (offset_start > 0 && offset_start >= subject_len)
ret = PCRE_ERROR_NOMATCH;
@@ -351,6 +362,7 @@ cond_pcre_match(char **a, int id)
const char *pcre_err;
char *lhstr, *rhre, *lhstr_plain, *rhre_plain, *avar=NULL;
int r = 0, pcre_opts = 0, pcre_errptr, capcnt, *ov, ovsize;
+ int lhstr_plain_len, rhre_plain_len;
int return_value = 0;
if (zpcre_utf8_enabled())
@@ -362,8 +374,8 @@ cond_pcre_match(char **a, int id)
rhre = cond_str(a,1,0);
lhstr_plain = ztrdup(lhstr);
rhre_plain = ztrdup(rhre);
- unmetafy(lhstr_plain, NULL);
- unmetafy(rhre_plain, NULL);
+ unmetafy(lhstr_plain, &lhstr_plain_len);
+ unmetafy(rhre_plain, &rhre_plain_len);
pcre_pat = NULL;
ov = NULL;
ovsize = 0;
@@ -373,6 +385,9 @@ cond_pcre_match(char **a, int id)
switch(id) {
case CPCRE_PLAIN:
+ if ((int)strlen(rhre_plain) != rhre_plain_len) {
+ zwarn("embedded NULs in PCRE pattern terminate pattern");
+ }
pcre_pat = pcre_compile(rhre_plain, pcre_opts, &pcre_err, &pcre_errptr, NULL);
if (pcre_pat == NULL) {
zwarn("failed to compile regexp /%s/: %s", rhre, pcre_err);
@@ -381,7 +396,7 @@ cond_pcre_match(char **a, int id)
pcre_fullinfo(pcre_pat, NULL, PCRE_INFO_CAPTURECOUNT, &capcnt);
ovsize = (capcnt+1)*3;
ov = zalloc(ovsize*sizeof(int));
- r = pcre_exec(pcre_pat, NULL, lhstr_plain, strlen(lhstr_plain), 0, 0, ov, ovsize);
+ r = pcre_exec(pcre_pat, NULL, lhstr_plain, lhstr_plain_len, 0, 0, ov, ovsize);
/* r < 0 => error; r==0 match but not enough size in ov
* r > 0 => (r-1) substrings found; r==1 => no substrings
*/
diff --git a/Src/Modules/regex.c b/Src/Modules/regex.c
index edb7234d4..d02769ef0 100644
--- a/Src/Modules/regex.c
+++ b/Src/Modules/regex.c
@@ -111,7 +111,7 @@ zcond_regex_match(char **a, int id)
*x = NULL;
}
if (isset(BASHREMATCH)) {
- setaparam("BASH_REMATCH", arr);
+ assignaparam("BASH_REMATCH", arr, 0);
} else {
zlong offs;
char *ptr;
@@ -119,7 +119,7 @@ zcond_regex_match(char **a, int id)
m = matches;
s = metafy(lhstr + m->rm_so, m->rm_eo - m->rm_so, META_DUP);
- setsparam("MATCH", s);
+ assignsparam("MATCH", s, 0);
/*
* Count the characters before the match.
*/
@@ -133,7 +133,7 @@ zcond_regex_match(char **a, int id)
ptr += clen;
leftlen -= clen;
}
- setiparam("MBEGIN", offs + !isset(KSHARRAYS));
+ assigniparam("MBEGIN", offs + !isset(KSHARRAYS), 0);
/*
* Add on the characters in the match.
*/
@@ -144,7 +144,7 @@ zcond_regex_match(char **a, int id)
ptr += clen;
leftlen -= clen;
}
- setiparam("MEND", offs + !isset(KSHARRAYS) - 1);
+ assigniparam("MEND", offs + !isset(KSHARRAYS) - 1, 0);
if (nelem) {
char **mbegin, **mend, **bptr, **eptr;
bptr = mbegin = (char **)zalloc(sizeof(char *)*(nelem+1));
diff --git a/Src/Modules/system.c b/Src/Modules/system.c
index afaec262a..3eecd7e95 100644
--- a/Src/Modules/system.c
+++ b/Src/Modules/system.c
@@ -313,7 +313,7 @@ bin_sysopen(char *nam, char **args, Options ops, UNUSED(int func))
int flags = O_NOCTTY | append | ((append || write) ?
(read ? O_RDWR : O_WRONLY) : O_RDONLY);
char *opt, *ptr, *nextopt, *fdvar;
- int o, fd, explicit = -1;
+ int o, fd, moved_fd, explicit = -1;
mode_t perms = 0666;
#if defined(FD_CLOEXEC) && !defined(O_CLOEXEC)
int fdflags;
@@ -376,22 +376,32 @@ bin_sysopen(char *nam, char **args, Options ops, UNUSED(int func))
zwarnnam(nam, "can't open file %s: %e", *args, errno);
return 1;
}
- fd = (explicit > -1) ? redup(fd, explicit) : movefd(fd);
- if (fd == -1) {
+ moved_fd = (explicit > -1) ? redup(fd, explicit) : movefd(fd);
+ if (moved_fd == -1) {
zwarnnam(nam, "can't open file %s", *args);
return 1;
}
-#if defined(FD_CLOEXEC) && !defined(O_CLOEXEC)
+#ifdef FD_CLOEXEC
+#ifdef O_CLOEXEC
+ /*
+ * the O_CLOEXEC is a flag attached to the *file descriptor*, not the
+ * *open file description* so it doesn't survive a dup(). If that flag was
+ * requested and the fd was moved, we need to reapply it to the moved fd
+ * even if the original one was open with O_CLOEXEC
+ */
+ if ((flags & O_CLOEXEC) && fd != moved_fd)
+#else
if (fdflags)
- fcntl(fd, F_SETFD, FD_CLOEXEC);
-#endif
+#endif /* O_CLOEXEC */
+ fcntl(moved_fd, F_SETFD, FD_CLOEXEC);
+#endif /* FD_CLOEXEC */
if (explicit == -1) {
- fdtable[fd] = FDT_EXTERNAL;
- setiparam(fdvar, fd);
- /* if setting the variable failed, close fd to avoid leak */
+ fdtable[moved_fd] = FDT_EXTERNAL;
+ setiparam(fdvar, moved_fd);
+ /* if setting the variable failed, close moved_fd to avoid leak */
if (errflag)
- zclose(fd);
+ zclose(moved_fd);
}
return 0;
diff --git a/Src/Modules/tcp.c b/Src/Modules/tcp.c
index dec12142b..0bbce5d49 100644
--- a/Src/Modules/tcp.c
+++ b/Src/Modules/tcp.c
@@ -343,7 +343,8 @@ bin_ztcp(char *nam, char **args, Options ops, UNUSED(int func))
{
int herrno, err=1, destport, force=0, verbose=0, test=0, targetfd=0;
ZSOCKLEN_T len;
- char **addrp, *desthost, *localname, *remotename;
+ char **addrp, *desthost;
+ const char *localname, *remotename;
struct hostent *zthost = NULL, *ztpeer = NULL;
struct servent *srv;
Tcp_session sess = NULL;
diff --git a/Src/Modules/zftp.c b/Src/Modules/zftp.c
index deed35e2f..24f4b4200 100644
--- a/Src/Modules/zftp.c
+++ b/Src/Modules/zftp.c
@@ -3177,7 +3177,7 @@ static struct features module_features = {
int
setup_(UNUSED(Module m))
{
- return (require_module("zsh/net/tcp", NULL) == 1);
+ return (require_module("zsh/net/tcp", NULL, 0) == 1);
}
/**/
diff --git a/Src/Modules/zpty.c b/Src/Modules/zpty.c
index 0ef753915..3c1bef58f 100644
--- a/Src/Modules/zpty.c
+++ b/Src/Modules/zpty.c
@@ -331,6 +331,7 @@ newptycmd(char *nam, char *pname, char **args, int echo, int nblock)
/* This code copied from the clone module, except for getting *
* the descriptor from get_pty() and duplicating it to 0/1/2. */
+ deletehookfunc("exit", ptyhook);
clearjobtab(0);
ppid = getppid();
mypid = getpid();
@@ -544,7 +545,8 @@ ptyread(char *nam, Ptycmd cmd, char **args, int noblock, int mustmatch)
p = dupstring(args[1]);
tokenize(p);
remnulargs(p);
- if (!(prog = patcompile(p, PAT_STATIC, NULL))) {
+ /* Signals handlers might stomp PAT_STATIC */
+ if (!(prog = patcompile(p, PAT_ZDUP, NULL))) {
zwarnnam(nam, "bad pattern: %s", args[1]);
return 1;
}
@@ -682,9 +684,14 @@ ptyread(char *nam, Ptycmd cmd, char **args, int noblock, int mustmatch)
write_loop(1, buf, used);
}
- if (seen && (!prog || matchok || !mustmatch))
- return 0;
- return cmd->fin + 1;
+ {
+ int ret = cmd->fin + 1;
+ if (seen && (!prog || matchok || !mustmatch))
+ ret = 0;
+ if (prog)
+ freepatprog(prog);
+ return ret;
+ }
}
static int
@@ -846,6 +853,7 @@ bin_zpty(char *nam, char **args, Options ops, UNUSED(int func))
}
}
+/**/
static int
ptyhook(UNUSED(Hookdef d), UNUSED(void *dummy))
{
diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c
index d95c0c254..19a8306b5 100644
--- a/Src/Modules/zutil.c
+++ b/Src/Modules/zutil.c
@@ -510,25 +510,33 @@ bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
zwarnnam(nam, "too many arguments");
return 1;
}
+
+ queue_signals(); /* Protect PAT_STATIC */
+
if (context) {
tokenize(context);
zstyle_contprog = patcompile(context, PAT_STATIC, NULL);
- if (!zstyle_contprog)
+ if (!zstyle_contprog) {
+ unqueue_signals();
return 1;
+ }
} else
zstyle_contprog = NULL;
if (stylename) {
s = (Style)zstyletab->getnode2(zstyletab, stylename);
- if (!s)
+ if (!s) {
+ unqueue_signals();
return 1;
+ }
zstyletab->printnode(&s->node, list);
} else {
scanhashtable(zstyletab, 1, 0, 0,
zstyletab->printnode, list);
}
+ unqueue_signals();
return 0;
}
switch (args[0][1]) {
@@ -675,14 +683,20 @@ bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
char **vals;
Patprog prog;
+ queue_signals(); /* Protect PAT_STATIC */
+
tokenize(args[3]);
if ((vals = lookupstyle(args[1], args[2])) &&
(prog = patcompile(args[3], PAT_STATIC, NULL))) {
while (*vals)
- if (pattry(prog, *vals++))
+ if (pattry(prog, *vals++)) {
+ unqueue_signals();
return 0;
+ }
}
+
+ unqueue_signals();
return 1;
}
break;
diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c
index d1cf7a08a..52b0c173f 100644
--- a/Src/Zle/compcore.c
+++ b/Src/Zle/compcore.c
@@ -3135,7 +3135,9 @@ matchcmp(Cmatch *a, Cmatch *b)
if ((*b)->disp && !((*b)->flags & CMF_MORDER))
return 1;
- return zstrbcmp((*a)->str, (*b)->str);
+ return zstrcmp((*a)->str, (*b)->str, (SORTIT_IGNORING_BACKSLASHES|
+ (isset(NUMERICGLOBSORT) ?
+ SORTIT_NUMERICALLY : 0)));
}
/* This tests whether two matches are equal (would produce the same
diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c
index 52c6f1233..5414b8ff6 100644
--- a/Src/Zle/compctl.c
+++ b/Src/Zle/compctl.c
@@ -99,7 +99,7 @@ freecompctlp(HashNode hn)
}
/**/
-void
+static void
freecompctl(Compctl cc)
{
if (cc == &cc_default ||
@@ -142,7 +142,7 @@ freecompctl(Compctl cc)
}
/**/
-void
+static void
freecompcond(void *a)
{
Compcond cc = (Compcond) a;
@@ -186,7 +186,7 @@ freecompcond(void *a)
}
/**/
-int
+static int
compctlread(char *name, char **args, Options ops, char *reply)
{
char *buf, *bptr;
@@ -1564,6 +1564,8 @@ bin_compctl(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
Compctl cc = NULL;
int ret = 0;
+ queue_signals();
+
/* clear static flags */
cclist = 0;
showmask = 0;
@@ -1571,12 +1573,15 @@ bin_compctl(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
/* Parse all the arguments */
if (*argv) {
/* Let's see if this is a global matcher definition. */
- if ((ret = get_gmatcher(name, argv)))
+ if ((ret = get_gmatcher(name, argv))) {
+ unqueue_signals();
return ret - 1;
+ }
cc = (Compctl) zshcalloc(sizeof(*cc));
if (get_compctl(name, &argv, cc, 1, 0, 0)) {
freecompctl(cc);
+ unqueue_signals();
return 1;
}
@@ -1604,6 +1609,7 @@ bin_compctl(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
printcompctl((cclist & COMP_LIST) ? "" : "DEFAULT", &cc_default, 0, 0);
printcompctl((cclist & COMP_LIST) ? "" : "FIRST", &cc_first, 0, 0);
print_gmatcher((cclist & COMP_LIST));
+ unqueue_signals();
return ret;
}
@@ -1642,6 +1648,7 @@ bin_compctl(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
printcompctl("", &cc_first, 0, 0);
if (cclist & COMP_LISTMATCH)
print_gmatcher(COMP_LIST);
+ unqueue_signals();
return ret;
}
@@ -1656,6 +1663,7 @@ bin_compctl(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
compctl_process_cc(argv, cc);
}
+ unqueue_signals();
return ret;
}
@@ -1667,12 +1675,18 @@ bin_compctl(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
static int
bin_compcall(char *name, UNUSED(char **argv), Options ops, UNUSED(int func))
{
+ int ret;
+
if (incompfunc != 1) {
zwarnnam(name, "can only be called from completion function");
return 1;
}
- return makecomplistctl((OPT_ISSET(ops,'T') ? 0 : CFN_FIRST) |
- (OPT_ISSET(ops,'D') ? 0 : CFN_DEFAULT));
+
+ queue_signals();
+ ret = makecomplistctl((OPT_ISSET(ops,'T') ? 0 : CFN_FIRST) |
+ (OPT_ISSET(ops,'D') ? 0 : CFN_DEFAULT));
+ unqueue_signals();
+ return ret;
}
/*
@@ -1756,6 +1770,8 @@ ccmakehookfn(UNUSED(Hookdef dummy), struct ccmakedat *dat)
int onm = nmatches, odm = diffmatches, osi = movefd(0);
LinkNode n;
+ queue_signals();
+
/* We build a copy of the list of matchers to use to make sure that this
* works even if a shell function called from the completion code changes
* the global matchers. */
@@ -1851,6 +1867,7 @@ ccmakehookfn(UNUSED(Hookdef dummy), struct ccmakedat *dat)
redup(osi, 0);
dat->lst = 0;
+ unqueue_signals();
return 0;
}
if (lastmatches) {
@@ -1874,6 +1891,7 @@ ccmakehookfn(UNUSED(Hookdef dummy), struct ccmakedat *dat)
redup(osi, 0);
dat->lst = 0;
+ unqueue_signals();
return 0;
}
if (!m || !(m = m->next))
@@ -1883,6 +1901,8 @@ ccmakehookfn(UNUSED(Hookdef dummy), struct ccmakedat *dat)
}
redup(osi, 0);
dat->lst = 1;
+
+ unqueue_signals();
return 0;
}
@@ -2044,7 +2064,7 @@ maketildelist(void)
/* This does the check for compctl -x `n' and `N' patterns. */
/**/
-int
+static int
getcpat(char *str, int cpatindex, char *cpat, int class)
{
char *s, *t, *p;
diff --git a/Src/Zle/complete.c b/Src/Zle/complete.c
index 48fcd4751..68bdf2332 100644
--- a/Src/Zle/complete.c
+++ b/Src/Zle/complete.c
@@ -901,7 +901,7 @@ do_comp_vars(int test, int na, char *sa, int nb, char *sb, int mod)
return 0;
singsub(&sa);
- pp = patcompile(sa, PAT_STATIC, NULL);
+ pp = patcompile(sa, PAT_HEAPDUP, NULL);
for (i--, p = compwords + i; i >= 0; p--, i--) {
if (pattry(pp, *p)) {
@@ -955,7 +955,7 @@ do_comp_vars(int test, int na, char *sa, int nb, char *sb, int mod)
if (!na)
return 0;
- if (!(pp = patcompile(sa, PAT_STATIC, 0)))
+ if (!(pp = patcompile(sa, PAT_HEAPDUP, 0)))
return 0;
if (test == CVT_PREPAT) {
diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c
index 2edaf6eca..a83daeff9 100644
--- a/Src/Zle/complist.c
+++ b/Src/Zle/complist.c
@@ -1993,7 +1993,8 @@ complistmatches(UNUSED(Hookdef dummy), Chdata dat)
if (noselect > 0)
noselect = 0;
- if ((minfo.asked == 2 && mselect < 0) || nlnct >= zterm_lines) {
+ if ((minfo.asked == 2 && mselect < 0) || nlnct >= zterm_lines ||
+ errflag) {
showinglist = 0;
amatches = oamatches;
return (noselect = 1);
@@ -2333,11 +2334,6 @@ msearch(Cmatch **ptr, char *ins, int back, int rep, int *wrapp)
}
}
if (x == ex && y == ey) {
- if (wrap) {
- msearchstate = MS_FAILED | owrap;
- break;
- }
- msearchstate |= MS_WRAPPED;
if (back) {
x = mcols - 1;
@@ -2349,6 +2345,13 @@ msearch(Cmatch **ptr, char *ins, int back, int rep, int *wrapp)
}
ex = mcol;
ey = mline;
+
+ if (wrap || (x == ex && y == ey)) {
+ msearchstate = MS_FAILED | owrap;
+ break;
+ }
+
+ msearchstate |= MS_WRAPPED;
wrap = 1;
*wrapp = 1;
}
diff --git a/Src/Zle/compmatch.c b/Src/Zle/compmatch.c
index aedf463fc..1cdbb8a48 100644
--- a/Src/Zle/compmatch.c
+++ b/Src/Zle/compmatch.c
@@ -1548,27 +1548,11 @@ pattern_match(Cpattern p, char *s, Cpattern wp, char *ws)
{
convchar_t c, wc;
convchar_t ind, wind;
- int len = 0, wlen, mt, wmt;
-#ifdef MULTIBYTE_SUPPORT
- mbstate_t lstate, wstate;
-
- memset(&lstate, 0, sizeof(lstate));
- memset(&wstate, 0, sizeof(wstate));
-#endif
+ int len = 0, wlen = 0, mt, wmt;
while (p && wp && *s && *ws) {
/* First test the word character */
-#ifdef MULTIBYTE_SUPPORT
- wlen = mb_metacharlenconv_r(ws, &wc, &wstate);
-#else
- if (*ws == Meta) {
- wc = STOUC(ws[1]) ^ 32;
- wlen = 2;
- } else {
- wc = STOUC(*ws);
- wlen = 1;
- }
-#endif
+ wc = unmeta_one(ws, &wlen);
wind = pattern_match1(wp, wc, &wmt);
if (!wind)
return 0;
@@ -1576,18 +1560,7 @@ pattern_match(Cpattern p, char *s, Cpattern wp, char *ws)
/*
* Now the line character.
*/
-#ifdef MULTIBYTE_SUPPORT
- len = mb_metacharlenconv_r(s, &c, &lstate);
-#else
- /* We have the character itself. */
- if (*s == Meta) {
- c = STOUC(s[1]) ^ 32;
- len = 2;
- } else {
- c = STOUC(*s);
- len = 1;
- }
-#endif
+ c = unmeta_one(s, &len);
/*
* If either is "?", they match each other; no further tests.
* Apply this even if the character wasn't convertable;
@@ -1627,17 +1600,7 @@ pattern_match(Cpattern p, char *s, Cpattern wp, char *ws)
}
while (p && *s) {
-#ifdef MULTIBYTE_SUPPORT
- len = mb_metacharlenconv_r(s, &c, &lstate);
-#else
- if (*s == Meta) {
- c = STOUC(s[1]) ^ 32;
- len = 2;
- } else {
- c = STOUC(*s);
- len = 1;
- }
-#endif
+ c = unmeta_one(s, &len);
if (!pattern_match1(p, c, &mt))
return 0;
p = p->next;
@@ -1645,17 +1608,7 @@ pattern_match(Cpattern p, char *s, Cpattern wp, char *ws)
}
while (wp && *ws) {
-#ifdef MULTIBYTE_SUPPORT
- wlen = mb_metacharlenconv_r(ws, &wc, &wstate);
-#else
- if (*ws == Meta) {
- wc = STOUC(ws[1]) ^ 32;
- wlen = 2;
- } else {
- wc = STOUC(*ws);
- wlen = 1;
- }
-#endif
+ wc = unmeta_one(ws, &wlen);
if (!pattern_match1(wp, wc, &wmt))
return 0;
wp = wp->next;
diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c
index 192ddeab9..e704f9ffa 100644
--- a/Src/Zle/computil.c
+++ b/Src/Zle/computil.c
@@ -917,7 +917,6 @@ struct cadef {
int argsactive; /* if normal arguments are still allowed */
/* used while parsing a command line */
char *set; /* set name prefix (<name>-), shared */
- char *sname; /* set name */
int flags; /* see CDF_* below */
char *nonarg; /* pattern for non-args (-A argument) */
};
@@ -935,7 +934,7 @@ struct caopt {
Caarg args; /* option arguments */
int active; /* still allowed on command line */
int num; /* it's the num'th option */
- char *set; /* set name, shared */
+ char *gsname; /* group or set name, shared */
int not; /* don't complete this option (`!...') */
};
@@ -956,10 +955,10 @@ struct caarg {
char *end; /* end-pattern for ::<pat>:... */
char *opt; /* option name if for an option */
int num; /* it's the num'th argument */
- int min; /* it's also this argument, using opt. args */
+ int min; /* earliest possible arg pos, given optional args */
int direct; /* true if argument number was given explicitly */
int active; /* still allowed on command line */
- char *set; /* set name, shared */
+ char *gsname; /* group or set name, shared */
};
#define CAA_NORMAL 1
@@ -1020,7 +1019,6 @@ freecadef(Cadef d)
s = d->snext;
zsfree(d->match);
zsfree(d->set);
- zsfree(d->sname);
if (d->defs)
freearray(d->defs);
@@ -1098,7 +1096,7 @@ parse_caarg(int mult, int type, int num, int opt, char *oname, char **def,
ret->type = type;
ret->opt = ztrdup(oname);
ret->direct = 0;
- ret->set = set;
+ ret->gsname = set;
/* Get the description. */
@@ -1147,8 +1145,11 @@ alloc_cadef(char **args, int single, char *match, char *nonarg, int flags)
ret->defs = NULL;
ret->ndefs = 0;
}
+ ret->nopts = 0;
+ ret->ndopts = 0;
+ ret->nodopts = 0;
ret->lastt = time(0);
- ret->set = ret->sname = NULL;
+ ret->set = NULL;
if (single) {
ret->single = (Caopt *) zalloc(256 * sizeof(Caopt));
memset(ret->single, 0, 256 * sizeof(Caopt));
@@ -1182,12 +1183,10 @@ parse_cadef(char *nam, char **args)
Cadef all, ret;
Caopt *optp;
char **orig_args = args, *p, *q, *match = "r:|[_-]=* r:|=*", **xor, **sargs;
- char *adpre, *adsuf, *axor = NULL, *doset = NULL, **setp = NULL;
+ char *adpre, *adsuf, *axor = NULL, *doset = NULL, **pendset = NULL, **curset = NULL;
char *nonarg = NULL;
- int single = 0, anum = 1, xnum, nopts, ndopts, nodopts, flags = 0;
- int state = 0, not = 0;
-
- nopts = ndopts = nodopts = 0;
+ int single = 0, anum = 1, xnum, flags = 0;
+ int foreignset = 0, not = 0;
/* First string is the auto-description definition. */
@@ -1250,51 +1249,72 @@ parse_cadef(char *nam, char **args)
if (nonarg)
tokenize(nonarg = dupstring(nonarg));
-
/* Looks good. Optimistically allocate the cadef structure. */
all = ret = alloc_cadef(orig_args, single, match, nonarg, flags);
optp = &(ret->opts);
- anum = 1;
-
sargs = args;
/* Get the definitions. */
- for (; *args; args++) {
+ for (; *args || pendset; args++) {
+ if (!*args) {
+ /* start new set */
+ args = sargs; /* go back and repeat parse of common options */
+ doset = NULL;
+ set_cadef_opts(ret);
+ ret = ret->snext = alloc_cadef(NULL, single, match, nonarg, flags);
+ optp = &(ret->opts);
+ anum = 1;
+ foreignset = 0;
+ curset = pendset;
+ pendset = 0;
+ }
if (args[0][0] == '-' && !args[0][1] && args[1]) {
- if (!state) {
- char *p;
- int l;
-
- if (setp)
- args = setp;
- p = *++args;
- l = strlen(p) - 1;
+ if ((foreignset = curset && args != curset)) {
+ if (!pendset && args > curset)
+ pendset = args; /* mark pointer to next pending set */
+ ++args;
+ } else {
+ /* Carrying on: this is the current set */
+ char *p = *++args;
+ int l = strlen(p) - 1;
+
if (*p == '(' && p[l] == ')') {
axor = p = dupstring(p + 1);
p[l - 1] = '\0';
} else
axor = NULL;
+ if (!*p) {
+ freecadef(all);
+ zwarnnam(nam, "empty set name");
+ return NULL;
+ }
ret->set = doset = tricat(p, "-", "");
- ret->sname = ztrdup(p);
- state = 1;
- } else {
- setp = args;
- state = 0;
- args = sargs - 1;
- doset = NULL;
- ret->nopts = nopts;
- ret->ndopts = ndopts;
- ret->nodopts = nodopts;
- set_cadef_opts(ret);
- ret = ret->snext = alloc_cadef(NULL, single, NULL, nonarg, flags);
- optp = &(ret->opts);
- nopts = ndopts = nodopts = 0;
- anum = 1;
+ curset = args; /* needed for the first set */
}
continue;
- }
+ } else if (args[0][0] == '+' && !args[0][1] && args[1]) {
+ char *p;
+ int l;
+
+ foreignset = 0; /* group not in any set, don't want to skip it */
+ p = *++args;
+ l = strlen(p) - 1;
+ if (*p == '(' && p[l] == ')') {
+ axor = p = dupstring(p + 1);
+ p[l - 1] = '\0';
+ } else
+ axor = NULL;
+ if (!*p) {
+ freecadef(all);
+ zwarnnam(nam, "empty group name");
+ return NULL;
+ }
+ doset = tricat(p, "-", "");
+ continue;
+ } else if (foreignset) /* skipping over a different set */
+ continue;
p = dupstring(*args);
xnum = 0;
if ((not = (*p == '!')))
@@ -1506,7 +1526,7 @@ parse_cadef(char *nam, char **args)
optp = &((*optp)->next);
opt->next = NULL;
- opt->set = doset;
+ opt->gsname = doset;
opt->name = ztrdup(rembslashcolon(name));
if (descr)
opt->descr = ztrdup(descr);
@@ -1526,13 +1546,13 @@ parse_cadef(char *nam, char **args)
opt->xor = (again == 1 && xor ? zarrdup(xor) : xor);
opt->type = otype;
opt->args = oargs;
- opt->num = nopts++;
+ opt->num = ret->nopts++;
opt->not = not;
if (otype == CAO_DIRECT || otype == CAO_EQUAL)
- ndopts++;
+ ret->ndopts++;
else if (otype == CAO_ODIRECT || otype == CAO_OEQUAL)
- nodopts++;
+ ret->nodopts++;
/* If this is for single-letter option we also store a
* pointer for the definition in the array for fast lookup.
@@ -1584,7 +1604,7 @@ parse_cadef(char *nam, char **args)
continue;
if ((direct = idigit(*p))) {
- /* Argment number is given. */
+ /* Argument number is given. */
int num = 0;
while (*p && idigit(*p))
@@ -1630,9 +1650,6 @@ parse_cadef(char *nam, char **args)
ret->args = arg;
}
}
- ret->nopts = nopts;
- ret->ndopts = ndopts;
- ret->nodopts = nodopts;
set_cadef_opts(ret);
return all;
@@ -1751,6 +1768,27 @@ ca_get_sopt(Cadef d, char *line, char **end, LinkList *lp)
return pp;
}
+/* Search for an option in all sets except the current one.
+ * Return true if found */
+
+static int
+ca_foreign_opt(Cadef curset, Cadef all, char *option)
+{
+ Cadef d;
+ Caopt p;
+
+ for (d = all; d; d = d->snext) {
+ if (d == curset)
+ continue;
+
+ for (p = d->opts; p; p = p->next) {
+ if (!strcmp(p->name, option))
+ return 1;
+ }
+ }
+ return 0;
+}
+
/* Return the n'th argument definition. */
static Caarg
@@ -1776,77 +1814,95 @@ ca_get_arg(Cadef d, int n)
* d: option definitions for a set
* pass either:
* xor: a list if exclusions
- * opts: if set, all options excluded leaving only nornal/rest arguments
- * if ca_xor list initialised, exclusions are added to it */
-
-static LinkList ca_xor;
+ * opts: if set, all options excluded leaving only nornal/rest arguments */
-static int
-ca_inactive(Cadef d, char **xor, int cur, int opts, char *optname)
+static void
+ca_inactive(Cadef d, char **xor, int cur, int opts)
{
if ((xor || opts) && cur <= compcurrent) {
Caopt opt;
char *x;
- int sl = (d->set ? (int)strlen(d->set) : -1), set = 0;
+ /* current word could be a prefix of a longer one so only do
+ * exclusions for single-letter options (for option clumping) */
+ int single = (cur == compcurrent);
for (; (x = (opts ? "-" : *xor)); xor++) {
- if (optname && optname[0] == x[0] && strcmp(optname, x))
- continue;
- if (ca_xor)
- addlinknode(ca_xor, x);
- set = 0;
- if (sl > 0) {
- if (strpfx(d->set, x)) {
- x += sl;
- set = 1;
- } else if (!strncmp(d->set, x, sl - 1)) {
- Caopt p;
-
- for (p = d->opts; p; p = p->next)
- if (p->set)
- p->active = 0;
-
- x = ":";
- set = 1;
+ int excludeall = 0;
+ char *grp = NULL;
+ size_t grplen;
+ char *next, *sep = x;
+
+ while (*sep != '+' && *sep != '-' && *sep != ':' && *sep != '*' && !idigit(*sep)) {
+ if (!(next = strchr(sep, '-')) || !*++next) {
+ /* exclusion is just the name of a set or group */
+ excludeall = 1; /* excluding options and args */
+ sep += strlen(sep);
+ /* A trailing '-' is included in the various gsname fields but is not
+ * there for this branch. This is why we add excludeall to grplen
+ * when checking for the null in a few places below */
+ break;
}
+ sep = next;
+ }
+ if (sep > x) { /* exclusion included a set or group name */
+ grp = x;
+ grplen = sep - grp;
+ x = sep;
}
- if (x[0] == ':' && !x[1]) {
- if (set) {
+
+ if (excludeall || (x[0] == ':' && !x[1])) {
+ if (grp) {
Caarg a;
for (a = d->args; a; a = a->next)
- if (a->set)
+ if (a->gsname && !strncmp(a->gsname, grp, grplen) &&
+ !a->gsname[grplen + excludeall])
a->active = 0;
- if (d->rest && (!set || d->rest->set))
+ if (d->rest && d->rest->gsname &&
+ !strncmp(d->rest->gsname, grp, grplen) &&
+ !d->rest->gsname[grplen + excludeall])
d->rest->active = 0;
} else
d->argsactive = 0;
- } else if (x[0] == '-' && !x[1]) {
+ }
+
+ if (excludeall || (x[0] == '-' && !x[1])) {
Caopt p;
for (p = d->opts; p; p = p->next)
- if (!set || p->set)
+ if ((!grp || (p->gsname && !strncmp(p->gsname, grp, grplen) &&
+ !p->gsname[grplen + excludeall])) &&
+ !(single && *p->name && p->name[1] && p->name[2]))
p->active = 0;
- } else if (x[0] == '*' && !x[1]) {
- if (d->rest && (!set || d->rest->set))
- d->rest->active = 0;
- } else if (idigit(x[0])) {
- int n = atoi(x);
- Caarg a = d->args;
-
- while (a && a->num < n)
- a = a->next;
+ }
- if (a && a->num == n && (!set || a->set))
- a->active = 0;
- } else if ((opt = ca_get_opt(d, x, 1, NULL)) && (!set || opt->set))
- opt->active = 0;
+ if (excludeall || (x[0] == '*' && !x[1])) {
+ if (d->rest && (!grp || (d->rest->gsname &&
+ !strncmp(d->rest->gsname, grp, grplen) &&
+ !d->rest->gsname[grplen + excludeall])))
+ d->rest->active = 0;
+ }
- if (opts)
- break;
+ if (!excludeall) {
+ if (idigit(x[0])) {
+ int n = atoi(x);
+ Caarg a = d->args;
+
+ while (a && a->num < n)
+ a = a->next;
+
+ if (a && a->num == n && (!grp || (a->gsname &&
+ !strncmp(a->gsname, grp, grplen))))
+ a->active = 0;
+ } else if ((opt = ca_get_opt(d, x, 1, NULL)) &&
+ (!grp || (opt->gsname && !strncmp(opt->gsname, grp, grplen))) &&
+ !(single && *opt->name && opt->name[1] && opt->name[2]))
+ opt->active = 0;
+ if (opts)
+ break;
+ }
}
}
- return 0;
}
/* State when parsing a command line. */
@@ -1875,7 +1931,6 @@ struct castate {
int curpos; /* current word position */
int argend; /* total number of words */
int inopt; /* set to current word pos if word is a recognised option */
- int inrest; /* unused */
int inarg; /* in a normal argument */
int nth; /* number of current normal arg */
int doff; /* length of current option */
@@ -1934,7 +1989,7 @@ ca_opt_arg(Caopt opt, char *line)
* existing options on the line. */
static int
-ca_parse_line(Cadef d, int multi, int first)
+ca_parse_line(Cadef d, Cadef all, int multi, int first)
{
Caarg adef, ddef;
Caopt ptr, wasopt = NULL, dopt;
@@ -1978,7 +2033,7 @@ ca_parse_line(Cadef d, int multi, int first)
state.argbeg = state.optbeg = state.nargbeg = state.restbeg = state.actopts =
state.nth = state.inopt = state.inarg = state.opt = state.arg = 1;
state.argend = argend = arrlen(compwords) - 1;
- state.inrest = state.doff = state.singles = state.oopt = 0;
+ state.doff = state.singles = state.oopt = 0;
state.curpos = compcurrent;
state.args = znewlinklist();
state.oargs = (LinkList *) zalloc(d->nopts * sizeof(LinkList));
@@ -2025,10 +2080,9 @@ ca_parse_line(Cadef d, int multi, int first)
remnulargs(line);
untokenize(line);
- if (ca_inactive(d, argxor, cur, 0, NULL) ||
- ((d->flags & CDF_SEP) && cur != compcurrent && !strcmp(line, "--"))) {
- if (ca_inactive(d, NULL, cur, 1, NULL))
- return 1;
+ ca_inactive(d, argxor, cur, 0);
+ if ((d->flags & CDF_SEP) && cur != compcurrent && !strcmp(line, "--")) {
+ ca_inactive(d, NULL, cur, 1);
continue;
}
@@ -2104,9 +2158,7 @@ ca_parse_line(Cadef d, int multi, int first)
if (!state.oargs[state.curopt->num])
state.oargs[state.curopt->num] = znewlinklist();
- if (ca_inactive(d, state.curopt->xor, cur, 0,
- (cur == compcurrent ? state.curopt->name : NULL)))
- return 1;
+ ca_inactive(d, state.curopt->xor, cur, 0);
/* Collect the argument strings. Maybe. */
@@ -2159,9 +2211,7 @@ ca_parse_line(Cadef d, int multi, int first)
if (!state.oargs[tmpopt->num])
state.oargs[tmpopt->num] = znewlinklist();
- if (ca_inactive(d, tmpopt->xor, cur, 0,
- (cur == compcurrent ? tmpopt->name : NULL)))
- return 1;
+ ca_inactive(d, tmpopt->xor, cur, 0);
}
}
if (state.def &&
@@ -2183,20 +2233,13 @@ ca_parse_line(Cadef d, int multi, int first)
else
state.curopt = NULL;
} else if (multi && (*line == '-' || *line == '+') && cur != compcurrent
-#if 0
- /**** Ouch. Using this will disable the mutual exclusion
- of different sets. Not using it will make the -A
- pattern be effectively ignored with multiple sets. */
- && (!napat || !pattry(napat, line))
-#endif
- )
+ && (ca_foreign_opt(d, all, line)))
return 1;
else if (state.arg &&
(!napat || cur <= compcurrent || !pattry(napat, line))) {
/* Otherwise it's a normal argument. */
- if (napat && cur <= compcurrent &&
- ca_inactive(d, NULL, cur + 1, 1, NULL))
- return 1;
+ if (napat && cur <= compcurrent)
+ ca_inactive(d, NULL, cur + 1, 1);
arglast = 1;
/* if this is the first normal arg after an option, may have been
@@ -2231,7 +2274,6 @@ ca_parse_line(Cadef d, int multi, int first)
if (ca_laststate.def)
break;
- state.inrest = 0;
state.opt = (cur == state.nargbeg + 1 &&
(!multi || !*line ||
*line == '-' || *line == '+'));
@@ -2421,19 +2463,19 @@ ca_set_data(LinkList descr, LinkList act, LinkList subc,
restrict_range(ca_laststate.argbeg, ca_laststate.argend);
}
if (arg->opt) {
- buf = (char *) zhalloc((arg->set ? strlen(arg->set) : 0) +
+ buf = (char *) zhalloc((arg->gsname ? strlen(arg->gsname) : 0) +
strlen(arg->opt) + 40);
if (arg->num > 0 && arg->type < CAA_REST)
sprintf(buf, "%soption%s-%d",
- (arg->set ? arg->set : ""), arg->opt, arg->num);
+ (arg->gsname ? arg->gsname : ""), arg->opt, arg->num);
else
sprintf(buf, "%soption%s-rest",
- (arg->set ? arg->set : ""), arg->opt);
+ (arg->gsname ? arg->gsname : ""), arg->opt);
} else if (arg->num > 0) {
sprintf(nbuf, "argument-%d", arg->num);
- buf = (arg->set ? dyncat(arg->set, nbuf) : dupstring(nbuf));
+ buf = (arg->gsname ? dyncat(arg->gsname, nbuf) : dupstring(nbuf));
} else
- buf = (arg->set ? dyncat(arg->set, "argument-rest") :
+ buf = (arg->gsname ? dyncat(arg->gsname, "argument-rest") :
dupstring("argument-rest"));
addlinknode(subc, buf);
@@ -2537,47 +2579,29 @@ bin_comparguments(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
* auto-description string, the optional -s, -S, -A and -M options
* given to _arguments and the specs. */
if (compcurrent > 1 && compwords[0]) {
- Cadef def;
+ Cadef def, all;
int cap = ca_parsed, multi, first = 1, use, ret = 0;
- LinkList cax = ca_xor, nx;
- LinkNode node;
Castate states = NULL, sp;
- char *xor[2];
ca_parsed = 0;
- xor[1] = NULL;
- if (!(def = get_cadef(nam, args + 1)))
+ if (!(def = all = get_cadef(nam, args + 1)))
return 1;
multi = !!def->snext; /* if we have sets */
ca_parsed = cap;
- ca_xor = (multi ? newlinklist() : NULL);
while (def) { /* for each set */
- use = !ca_parse_line(def, multi, first);
- nx = ca_xor;
- ca_xor = NULL; /* don't want to duplicate the xors in the list */
- while ((def = def->snext)) {
- if (nx) {
- for (node = firstnode(nx); node; incnode(node)) {
- xor[0] = (char *) getdata(node);
- if (!strcmp(xor[0], def->sname) ||
- ca_inactive(def, xor, compcurrent, 0, NULL))
- break; /* exclude this whole set */
- }
- if (!node) /* continue with this set */
- break;
- }
- /* entire set was excluded, continue to next set */
- }
- ca_xor = nx;
+ use = !ca_parse_line(def, all, multi, first);
+ def = def->snext;
if (use && def) {
+ /* entry needed so save it into list */
sp = (Castate) zalloc(sizeof(*sp));
memcpy(sp, &ca_laststate, sizeof(*sp));
sp->snext = states;
states = sp;
} else if (!use && !def) {
+ /* final entry not needed */
if (states) {
freecastate(&ca_laststate);
memcpy(&ca_laststate, states, sizeof(*sp));
@@ -2589,7 +2613,6 @@ bin_comparguments(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
}
first = 0;
}
- ca_xor = cax;
ca_parsed = 1;
ca_laststate.snext = states;
@@ -2602,7 +2625,7 @@ bin_comparguments(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
* things _arguments has to execute at this place on the line (the
* sub-contexts are used as tags).
* The return value is particularly important here, it says if
- * there are arguments to completely at all. */
+ * there are arguments to complete at all. */
{
LinkList descr, act, subc;
Caarg arg;
@@ -2805,7 +2828,7 @@ bin_comparguments(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
for (s = lstate; s; s = s->snext)
for (o = s->d->opts, a = s->oargs; o; o = o->next, a++)
if (*a) {
- *p++ = (o->set ? tricat(o->set, o->name, "") :
+ *p++ = (o->gsname ? tricat(o->gsname, o->name, "") :
ztrdup(o->name));
*p++ = ca_colonlist(*a);
}
@@ -3546,8 +3569,8 @@ bin_compvalues(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
Cvval val = cv_get_val(cv_laststate.d, args[1]);
if (val && val->arg) {
- setsparam(args[2], val->arg->descr);
- setsparam(args[3], val->arg->action);
+ setsparam(args[2], ztrdup(val->arg->descr));
+ setsparam(args[3], ztrdup(val->arg->action));
if (args[4])
setsparam(args[4], ztrdup(val->name));
@@ -3905,6 +3928,8 @@ bin_comptry(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
if (*q) {
char *qq, *qqq;
+ queue_signals();
+
if (c)
*c = '\0';
@@ -3976,6 +4001,8 @@ bin_comptry(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
}
if (c)
*c = ':';
+
+ unqueue_signals();
}
}
if (num) {
@@ -4438,17 +4465,24 @@ cfp_matcher_pats(char *matcher, char *add)
if (m && m != pcm_err) {
char *tmp;
int al = strlen(add), zl = ztrlen(add), tl, cl;
- VARARR(Cmatcher, ms, zl);
+ VARARR(Cmatcher, ms, zl); /* One Cmatcher per character */
Cmatcher *mp;
Cpattern stopp;
int stopl = 0;
+ /* zl >= (number of wide characters) is guaranteed */
memset(ms, 0, zl * sizeof(Cmatcher));
for (; m && *add; m = m->next) {
stopp = NULL;
if (!(m->flags & (CMF_LEFT|CMF_RIGHT))) {
if (m->llen == 1 && m->wlen == 1) {
+ /*
+ * In this loop and similar loops below we step
+ * through tmp one (possibly wide) character at a
+ * time. pattern_match() compares only the first
+ * character using unmeta_one() so keep in step.
+ */
for (tmp = add, tl = al, mp = ms; tl; ) {
if (pattern_match(m->line, tmp, NULL, NULL)) {
if (*mp) {
@@ -4458,10 +4492,10 @@ cfp_matcher_pats(char *matcher, char *add)
} else
*mp = m;
}
- cl = (*tmp == Meta) ? 2 : 1;
+ (void) unmeta_one(tmp, &cl);
tl -= cl;
tmp += cl;
- mp += cl;
+ mp++;
}
} else {
stopp = m->line;
@@ -4478,10 +4512,10 @@ cfp_matcher_pats(char *matcher, char *add)
} else
*mp = m;
}
- cl = (*tmp == Meta) ? 2 : 1;
+ (void) unmeta_one(tmp, &cl);
tl -= cl;
tmp += cl;
- mp += cl;
+ mp++;
}
} else if (m->llen) {
stopp = m->line;
@@ -4504,7 +4538,7 @@ cfp_matcher_pats(char *matcher, char *add)
al = tmp - add;
break;
}
- cl = (*tmp == Meta) ? 2 : 1;
+ (void) unmeta_one(tmp, &cl);
tl -= cl;
tmp += cl;
}
@@ -4685,6 +4719,8 @@ cfp_add_sdirs(LinkList final, LinkList orig, char *skipped,
if (!*p)
continue;
+ queue_signals(); /* Protect PAT_STATIC */
+
tokenize(f);
pprog = patcompile(f, PAT_STATIC, NULL);
untokenize(f);
@@ -4717,6 +4753,8 @@ cfp_add_sdirs(LinkList final, LinkList orig, char *skipped,
}
}
}
+
+ unqueue_signals();
}
}
}
diff --git a/Src/Zle/textobjects.c b/Src/Zle/textobjects.c
index 3db0781ff..bf83906f2 100644
--- a/Src/Zle/textobjects.c
+++ b/Src/Zle/textobjects.c
@@ -48,9 +48,10 @@ int
selectword(UNUSED(char **args))
{
int n = zmult;
- int all = (bindk == t_selectaword || bindk == t_selectablankword);
- int (*viclass)(ZLE_CHAR_T) = (bindk == t_selectaword ||
- bindk == t_selectinword) ? wordclass : blankwordclass;
+ int all = IS_THINGY(bindk, selectaword) ||
+ IS_THINGY(bindk, selectablankword);
+ int (*viclass)(ZLE_CHAR_T) = (IS_THINGY(bindk, selectaword) ||
+ IS_THINGY(bindk, selectinword)) ? wordclass : blankwordclass;
int sclass = viclass(zleline[zlecs]);
int doblanks = all && sclass;
@@ -288,7 +289,7 @@ selectargument(UNUSED(char **args))
free(stringaszleline(linein, wstarts[wcur], &zlecs, &tmpsz, &mark));
free(linein);
- if (bindk == t_selectinshellword) {
+ if (IS_THINGY(bindk, selectinshellword)) {
ZLE_CHAR_T *match = ZWS("`\'\"");
ZLE_CHAR_T *lmatch = ZWS("\'({"), *rmatch = ZWS("\')}");
ZLE_CHAR_T *ematch = match, *found;
diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h
index 8f92e5611..07b310180 100644
--- a/Src/Zle/zle.h
+++ b/Src/Zle/zle.h
@@ -230,6 +230,13 @@ struct thingy {
/* DISABLED is (1<<0) */
#define TH_IMMORTAL (1<<1) /* can't refer to a different widget */
+/*
+ * Check if bindk refers to named thingy (a set of bare characters),
+ * also checking the special .thingy widget.
+ */
+#define IS_THINGY(bindk, name) \
+ ((bindk) == t_ ## name || (bindk) == t_D ## name)
+
/* command modifier prefixes */
struct modifier {
diff --git a/Src/Zle/zle_hist.c b/Src/Zle/zle_hist.c
index abd6e1749..581ca4979 100644
--- a/Src/Zle/zle_hist.c
+++ b/Src/Zle/zle_hist.c
@@ -1220,13 +1220,14 @@ doisearch(char **args, int dir, int pattern)
char *patbuf = ztrdup(sbuf);
char *patstring;
/*
- * Use static pattern buffer since we don't need
- * to maintain it and won't call other pattern functions
- * meanwhile.
- * Use PAT_NOANCH because we don't need the match
- * anchored to the end, even if it is at the start.
+ * Do not use static pattern buffer (PAT_STATIC) since we
+ * call zle hooks, which might call other pattern
+ * functions. Use PAT_ZDUP because we re-use the pattern
+ * in subsequent loops, so we can't pushheap/popheap.
+ * Use PAT_NOANCH because we don't need the match anchored
+ * to the end, even if it is at the start.
*/
- int patflags = PAT_STATIC|PAT_NOANCH;
+ int patflags = PAT_ZDUP|PAT_NOANCH;
if (sbuf[0] == '^') {
/*
* We'll handle the anchor later when
@@ -1521,6 +1522,7 @@ doisearch(char **args, int dir, int pattern)
if (only_one || !top_spot || old_sbptr != sbptr)
break;
}
+ freepatprog(patprog);
patprog = NULL;
nosearch = 1;
skip_pos = 0;
@@ -1632,6 +1634,7 @@ doisearch(char **args, int dir, int pattern)
}
strcpy(sbuf + sbptr, paste);
sbptr += pastelen;
+ freepatprog(patprog);
patprog = NULL;
free(paste);
} else if (cmd == Th(z_acceptsearch)) {
@@ -1682,6 +1685,7 @@ doisearch(char **args, int dir, int pattern)
* always valid at this point.
*/
sbptr += zlecharasstring(LASTFULLCHAR, sbuf + sbptr);
+ freepatprog(patprog);
patprog = NULL;
}
if (feep)
@@ -1702,6 +1706,7 @@ doisearch(char **args, int dir, int pattern)
zsfree(okeymap);
if (matchlist)
freematchlist(matchlist);
+ freepatprog(patprog);
isearch_active = 0;
/*
* Don't allow unused characters provided as a string to the
diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c
index 04eb70675..2e96ac780 100644
--- a/Src/Zle/zle_keymap.c
+++ b/Src/Zle/zle_keymap.c
@@ -961,7 +961,7 @@ bin_bindkey_meta(char *name, char *kmname, Keymap km, UNUSED(char **argv), UNUSE
m[0] = i;
metafy(m, 1, META_NOALLOC);
fn = keybind(km, m, &str);
- if(fn == t_selfinsert || fn == t_undefinedkey)
+ if(IS_THINGY(fn, selfinsert) || fn == t_undefinedkey)
bindkey(km, m, refthingy(Th(metabind[i - 128])), NULL);
}
return 0;
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index 15ea79643..be2b062b0 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -1245,6 +1245,7 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
resetneeded = 0;
fetchttyinfo = 0;
trashedzle = 0;
+ clearflag = 0;
raw_lp = lp;
lpromptbuf = promptexpand(lp ? *lp : NULL, 1, NULL, NULL, &pmpt_attr);
raw_rp = rp;
@@ -1484,6 +1485,13 @@ execzlefunc(Thingy func, char **args, int set_bindk)
int inuse = w->flags & WIDGET_INUSE;
w->flags |= WIDGET_INUSE;
+ if (osi > 0) {
+ /*
+ * Many commands don't like having a closed stdin, open on
+ * /dev/null instead
+ */
+ open("/dev/null", O_RDWR | O_NOCTTY); /* ignore failure */
+ }
if (*args) {
largs = newlinklist();
addlinknode(largs, dupstring(w->u.fnnam));
diff --git a/Src/Zle/zle_params.c b/Src/Zle/zle_params.c
index 1e4c5b832..0a922d2d6 100644
--- a/Src/Zle/zle_params.c
+++ b/Src/Zle/zle_params.c
@@ -85,6 +85,8 @@ static const struct gsu_integer cursor_gsu =
{ get_cursor, set_cursor, zleunsetfn };
static const struct gsu_integer histno_gsu =
{ get_histno, set_histno, zleunsetfn };
+static const struct gsu_integer keys_queued_count_gsu =
+{ get_keys_queued_count, NULL, zleunsetfn };
static const struct gsu_integer mark_gsu =
{ get_mark, set_mark, zleunsetfn };
static const struct gsu_integer numeric_gsu =
@@ -146,6 +148,8 @@ static struct zleparam {
{ "HISTNO", PM_INTEGER, GSU(histno_gsu), NULL },
{ "KEYMAP", PM_SCALAR | PM_READONLY, GSU(keymap_gsu), NULL },
{ "KEYS", PM_SCALAR | PM_READONLY, GSU(keys_gsu), NULL },
+ { "KEYS_QUEUED_COUNT", PM_INTEGER | PM_READONLY, GSU(keys_queued_count_gsu),
+ NULL},
{ "killring", PM_ARRAY, GSU(killring_gsu), NULL },
{ "LASTABORTEDSEARCH", PM_SCALAR | PM_READONLY, GSU(lastabortedsearch_gsu),
NULL },
@@ -458,6 +462,13 @@ get_keys(UNUSED(Param pm))
}
/**/
+static zlong
+get_keys_queued_count(UNUSED(Param pm))
+{
+ return kungetct;
+}
+
+/**/
static void
set_numeric(UNUSED(Param pm), zlong x)
{
diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c
index 8d173cda1..d0dd1ef06 100644
--- a/Src/Zle/zle_refresh.c
+++ b/Src/Zle/zle_refresh.c
@@ -1278,7 +1278,7 @@ zrefresh(void)
#ifdef __STDC_ISO_10646__
!ZSH_INVALID_WCHAR_TEST(*t) &&
#endif
- iswprint(*t) && (width = WCWIDTH(*t)) > 0) {
+ WC_ISPRINT(*t) && (width = WCWIDTH(*t)) > 0) {
int ichars;
if (width > rpms.sen - rpms.s) {
int started = 0;
@@ -1460,7 +1460,7 @@ zrefresh(void)
u = outputline;
for (; u < outputline + outll; u++) {
#ifdef MULTIBYTE_SUPPORT
- if (iswprint(*u)) {
+ if (WC_ISPRINT(*u)) {
int width = WCWIDTH(*u);
/* Handle wide characters as above */
if (width > rpms.sen - rpms.s) {
@@ -2434,8 +2434,8 @@ redisplay(UNUSED(char **args))
moveto(0, 0);
zputc(&zr_cr); /* extra care */
tc_upcurs(lprompth - 1);
- resetneeded = !showinglist;
- clearflag = showinglist;
+ resetneeded = 1;
+ clearflag = 0;
return 0;
}
@@ -2468,7 +2468,7 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs)
if (tmpline[t0] == ZWC('\t'))
vsiz = (vsiz | 7) + 2;
#ifdef MULTIBYTE_SUPPORT
- else if (iswprint(tmpline[t0]) && ((width = WCWIDTH(tmpline[t0])) > 0)) {
+ else if (WC_ISPRINT(tmpline[t0]) && ((width = WCWIDTH(tmpline[t0])) > 0)) {
vsiz += width;
if (isset(COMBININGCHARS) && IS_BASECHAR(tmpline[t0])) {
while (t0 < tmpll-1 && IS_COMBINING(tmpline[t0+1]))
@@ -2556,7 +2556,7 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs)
vp->atr = all_atr_on | all_atr_off;
vp++;
#ifdef MULTIBYTE_SUPPORT
- } else if (iswprint(tmpline[t0]) &&
+ } else if (WC_ISPRINT(tmpline[t0]) &&
(width = WCWIDTH(tmpline[t0])) > 0) {
int ichars;
if (isset(COMBININGCHARS) && IS_BASECHAR(tmpline[t0])) {
diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c
index c7092854a..f7e9829c2 100644
--- a/Src/Zle/zle_thingy.c
+++ b/Src/Zle/zle_thingy.c
@@ -602,7 +602,7 @@ bin_zle_complete(char *name, char **args, UNUSED(Options ops), UNUSED(char func)
Thingy t;
Widget w, cw;
- if (require_module("zsh/complete", NULL) == 1) {
+ if (require_module("zsh/complete", NULL, 0) == 1) {
zwarnnam(name, "can't load complete module");
return 1;
}
@@ -703,7 +703,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;
+ int ret, saveflag = 0, setbindk = 0, remetafy;
char *wname = *args++, *keymap_restore = NULL, *keymap_tmp;
if (!wname)
@@ -714,7 +714,15 @@ bin_zle_call(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
return 1;
}
- UNMETACHECK();
+ /*
+ * zle is callable in traps, so we can't be sure the line is
+ * in its normal state.
+ */
+ if (zlemetaline) {
+ unmetafy_line();
+ remetafy = 1;
+ } else
+ remetafy = 0;
while (*args && **args == '-') {
char *num;
@@ -728,6 +736,8 @@ bin_zle_call(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
num = args[0][1] ? args[0]+1 : args[1];
if (!num) {
zwarnnam(name, "number expected after -%c", **args);
+ if (remetafy)
+ metafy_line();
return 1;
}
if (!args[0][1])
@@ -745,19 +755,26 @@ bin_zle_call(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
keymap_tmp = args[0][1] ? args[0]+1 : args[1];
if (!keymap_tmp) {
zwarnnam(name, "keymap expected after -%c", **args);
+ if (remetafy)
+ metafy_line();
return 1;
}
if (!args[0][1])
*++args = "" - 1;
keymap_restore = dupstring(curkeymapname);
- if (selectkeymap(keymap_tmp, 0))
+ if (selectkeymap(keymap_tmp, 0)) {
+ if (remetafy)
+ metafy_line();
return 1;
+ }
break;
case 'w':
setbindk = 1;
break;
default:
zwarnnam(name, "unknown option: %s", *args);
+ if (remetafy)
+ metafy_line();
return 1;
}
}
@@ -775,6 +792,8 @@ bin_zle_call(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
zmod = modsave;
if (keymap_restore)
selectkeymap(keymap_restore, 0);
+ if (remetafy)
+ metafy_line();
return ret;
}
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index 3d8679119..5a9cccb6f 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -2407,53 +2407,6 @@ sfxlen(char *s, char *t)
}
#endif
-/* This is zstrcmp with ignoring backslashes. */
-
-/**/
-mod_export int
-zstrbcmp(const char *a, const char *b)
-{
- const char *astart = a;
-
- while (*a && *b) {
- if (*a == '\\')
- a++;
- if (*b == '\\')
- b++;
- if (*a != *b || !*a)
- break;
- a++;
- b++;
- }
- if (isset(NUMERICGLOBSORT) && (idigit(*a) || idigit(*b))) {
- for (; a > astart && idigit(a[-1]); a--, b--);
- if (idigit(*a) && idigit(*b)) {
- while (*a == '0')
- a++;
- while (*b == '0')
- b++;
- for (; idigit(*a) && *a == *b; a++, b++);
- if (idigit(*a) || idigit(*b)) {
- int cmp = (int) STOUC(*a) - (int) STOUC(*b);
-
- while (idigit(*a) && idigit(*b))
- a++, b++;
- if (idigit(*a) && !idigit(*b))
- return 1;
- if (idigit(*b) && !idigit(*a))
- return -1;
-
- return cmp;
- }
- }
- }
-#ifndef HAVE_STRCOLL
- return (int)(*a - *b);
-#else
- return strcoll(a,b);
-#endif
-}
-
/* This is used to print the strings (e.g. explanations). *
* It returns the number of lines printed. */
diff --git a/Src/builtin.c b/Src/builtin.c
index 0f04d149f..2e72ba20a 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -46,7 +46,7 @@ static struct builtin builtins[] =
BUILTIN(".", BINF_PSPECIAL, bin_dot, 1, -1, 0, NULL, NULL),
BUILTIN(":", BINF_PSPECIAL, bin_true, 0, -1, 0, NULL, NULL),
BUILTIN("alias", BINF_MAGICEQUALS | BINF_PLUSOPTS, bin_alias, 0, -1, 0, "Lgmrs", NULL),
- BUILTIN("autoload", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "mktTUwXz", "u"),
+ BUILTIN("autoload", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "dmktrRTUwWXz", "u"),
BUILTIN("bg", 0, bin_fg, 0, -1, BIN_BG, NULL, NULL),
BUILTIN("break", BINF_PSPECIAL, bin_break, 0, 1, BIN_BREAK, NULL, NULL),
BUILTIN("bye", 0, bin_break, 0, 1, BIN_EXIT, NULL, NULL),
@@ -72,7 +72,7 @@ static struct builtin builtins[] =
BUILTIN("fc", 0, bin_fc, 0, -1, BIN_FC, "aAdDe:EfiIlLmnpPrRt: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:%ghlprtux", "E"),
- BUILTIN("functions", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "kmMtTuUx:z", NULL),
+ BUILTIN("functions", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "kmMstTuUWx:z", NULL),
BUILTIN("getln", 0, bin_read, 0, -1, 0, "ecnAlE", "zr"),
BUILTIN("getopts", 0, bin_getopts, 2, -1, 0, NULL, NULL),
BUILTIN("hash", BINF_MAGICEQUALS, bin_hash, 0, -1, 0, "Ldfmrv", NULL),
@@ -131,7 +131,7 @@ static struct builtin builtins[] =
BUILTIN("whence", 0, bin_whence, 0, -1, 0, "acmpvfsSwx:", NULL),
BUILTIN("where", 0, bin_whence, 0, -1, 0, "pmsSwx:", "ca"),
BUILTIN("which", 0, bin_whence, 0, -1, 0, "ampsSwx:", "c"),
- BUILTIN("zmodload", 0, bin_zmodload, 0, -1, 0, "AFRILP:abcfdilmpue", NULL),
+ BUILTIN("zmodload", 0, bin_zmodload, 0, -1, 0, "AFRILP:abcfdilmpsue", NULL),
BUILTIN("zcompile", 0, bin_zcompile, 0, -1, 0, "tUMRcmzka", NULL),
};
@@ -539,18 +539,18 @@ bin_enable(char *name, char **argv, Options ops, int func)
/* With -m option, treat arguments as glob patterns. */
if (OPT_ISSET(ops,'m')) {
for (; *argv; argv++) {
+ queue_signals();
+
/* parse pattern */
tokenize(*argv);
- if ((pprog = patcompile(*argv, PAT_STATIC, 0))) {
- queue_signals();
+ if ((pprog = patcompile(*argv, PAT_STATIC, 0)))
match += scanmatchtable(ht, pprog, 0, 0, 0, scanfunc, 0);
- unqueue_signals();
- }
else {
untokenize(*argv);
zwarnnam(name, "bad pattern : %s", *argv);
returnval = 1;
}
+ unqueue_signals();
}
/* If we didn't match anything, we return 1. */
if (!match)
@@ -796,8 +796,8 @@ set_pwd_env(void)
unsetparam_pm(pm, 0, 1);
}
- setsparam("PWD", ztrdup(pwd));
- setsparam("OLDPWD", ztrdup(oldpwd));
+ assignsparam("PWD", ztrdup(pwd), 0);
+ assignsparam("OLDPWD", ztrdup(oldpwd), 0);
pm = (Param) paramtab->getnode(paramtab, "PWD");
if (!(pm->node.flags & PM_EXPORTED))
@@ -880,8 +880,13 @@ cd_get_dest(char *nam, char **argv, int hard, int func)
dir = nextnode(firstnode(dirstack));
if (dir)
zinsertlinknode(dirstack, dir, getlinknode(dirstack));
- else if (func != BIN_POPD)
+ else if (func != BIN_POPD) {
+ if (!home) {
+ zwarnnam(nam, "HOME not set");
+ return NULL;
+ }
zpushnode(dirstack, ztrdup(home));
+ }
} else if (!argv[1]) {
int dd;
char *end;
@@ -936,6 +941,10 @@ cd_get_dest(char *nam, char **argv, int hard, int func)
if (!dir) {
dir = firstnode(dirstack);
}
+ if (!dir || !getdata(dir)) {
+ DPUTS(1, "Directory not set, not detected early enough");
+ return NULL;
+ }
if (!(dest = cd_do_chdir(nam, getdata(dir), hard))) {
if (!target)
zsfree(getlinknode(dirstack));
@@ -2922,9 +2931,61 @@ eval_autoload(Shfunc shf, char *name, Options ops, int func)
}
return !loadautofn(shf, (OPT_ISSET(ops,'k') ? 2 :
- (OPT_ISSET(ops,'z') ? 0 : 1)), 1);
+ (OPT_ISSET(ops,'z') ? 0 : 1)), 1,
+ OPT_ISSET(ops,'d'));
}
+/* Helper for bin_functions() for -X and -r options */
+
+/**/
+static int
+check_autoload(Shfunc shf, char *name, Options ops, int func)
+{
+ if (OPT_ISSET(ops,'X'))
+ {
+ return eval_autoload(shf, name, ops, func);
+ }
+ if ((OPT_ISSET(ops,'r') || OPT_ISSET(ops,'R')) &&
+ (shf->node.flags & PM_UNDEFINED))
+ {
+ char *dir_path;
+ if (shf->filename && (shf->node.flags & PM_LOADDIR)) {
+ char *spec_path[2];
+ spec_path[0] = shf->filename;
+ spec_path[1] = NULL;
+ if (getfpfunc(shf->node.nam, NULL, &dir_path, spec_path, 1)) {
+ /* shf->filename is already correct. */
+ return 0;
+ }
+ if (!OPT_ISSET(ops,'d')) {
+ if (OPT_ISSET(ops,'R')) {
+ zerr("%s: function definition file not found",
+ shf->node.nam);
+ return 1;
+ }
+ return 0;
+ }
+ }
+ if (getfpfunc(shf->node.nam, NULL, &dir_path, NULL, 1)) {
+ dircache_set(&shf->filename, NULL);
+ if (*dir_path != '/') {
+ dir_path = zhtricat(metafy(zgetcwd(), -1, META_HEAPDUP),
+ "/", dir_path);
+ dir_path = xsymlink(dir_path, 1);
+ }
+ dircache_set(&shf->filename, dir_path);
+ shf->node.flags |= PM_LOADDIR;
+ return 0;
+ }
+ if (OPT_ISSET(ops,'R')) {
+ zerr("%s: function definition file not found",
+ shf->node.nam);
+ return 1;
+ }
+ /* with -r, we don't flag an error, just let it be found later. */
+ }
+ return 0;
+}
/* List a user-defined math function. */
static void
@@ -2941,7 +3002,7 @@ listusermathfunc(MathFunc p)
else
showargs = 0;
- printf("functions -M %s", p->name);
+ printf("functions -M%s %s", (p->flags & MFF_STR) ? "s" : "", p->name);
if (showargs) {
printf(" %d", p->minargs);
showargs--;
@@ -2962,6 +3023,66 @@ listusermathfunc(MathFunc p)
}
+static void
+add_autoload_function(Shfunc shf, char *funcname)
+{
+ char *nam;
+ if (*funcname == '/' && funcname[1] &&
+ (nam = strrchr(funcname, '/')) && nam[1] &&
+ (shf->node.flags & PM_UNDEFINED)) {
+ char *dir;
+ nam = strrchr(funcname, '/');
+ if (nam == funcname) {
+ dir = "/";
+ } else {
+ *nam++ = '\0';
+ dir = funcname;
+ }
+ dircache_set(&shf->filename, NULL);
+ dircache_set(&shf->filename, dir);
+ shf->node.flags |= PM_LOADDIR;
+ shf->node.flags |= PM_ABSPATH_USED;
+ shfunctab->addnode(shfunctab, ztrdup(nam), shf);
+ } else {
+ Shfunc shf2;
+ Funcstack fs;
+ const char *calling_f = NULL;
+ char buf[PATH_MAX+1];
+
+ /* Find calling function */
+ for (fs = funcstack; fs; fs = fs->prev) {
+ if (fs->tp == FS_FUNC && fs->name && (!shf->node.nam || 0 != strcmp(fs->name,shf->node.nam))) {
+ calling_f = fs->name;
+ break;
+ }
+ }
+
+ /* Get its directory */
+ if (calling_f) {
+ /* Should contain load directory, and be loaded via absolute path */
+ if ((shf2 = (Shfunc) shfunctab->getnode2(shfunctab, calling_f))
+ && (shf2->node.flags & PM_LOADDIR) && (shf2->node.flags & PM_ABSPATH_USED)
+ && shf2->filename)
+ {
+ if (strlen(shf2->filename) + strlen(funcname) + 1 < PATH_MAX)
+ {
+ sprintf(buf, "%s/%s", shf2->filename, funcname);
+ /* Set containing directory if the function file
+ * exists (do normal FPATH processing otherwise) */
+ if (!access(buf, R_OK)) {
+ dircache_set(&shf->filename, NULL);
+ dircache_set(&shf->filename, shf2->filename);
+ shf->node.flags |= PM_LOADDIR;
+ shf->node.flags |= PM_ABSPATH_USED;
+ }
+ }
+ }
+ }
+
+ shfunctab->addnode(shfunctab, ztrdup(funcname), shf);
+ }
+}
+
/* Display or change the attributes of shell functions. *
* If called as autoload, it will define a new autoloaded *
* (undefined) shell function. */
@@ -2992,6 +3113,10 @@ bin_functions(char *name, char **argv, Options ops, int func)
on |= PM_TAGGED_LOCAL;
else if (OPT_PLUS(ops,'T'))
off |= PM_TAGGED_LOCAL;
+ if (OPT_MINUS(ops,'W'))
+ on |= PM_WARNNESTED;
+ else if (OPT_PLUS(ops,'W'))
+ off |= PM_WARNNESTED;
roff = off;
if (OPT_MINUS(ops,'z')) {
on |= PM_ZSHSTORED;
@@ -3007,10 +3132,17 @@ bin_functions(char *name, char **argv, Options ops, int func)
off |= PM_KSHSTORED;
roff |= PM_KSHSTORED;
}
+ if (OPT_MINUS(ops,'d')) {
+ on |= PM_CUR_FPATH;
+ off |= PM_CUR_FPATH;
+ } else if (OPT_PLUS(ops,'d')) {
+ off |= PM_CUR_FPATH;
+ roff |= PM_CUR_FPATH;
+ }
if ((off & PM_UNDEFINED) || (OPT_ISSET(ops,'k') && OPT_ISSET(ops,'z')) ||
(OPT_ISSET(ops,'x') && !OPT_HASARG(ops,'x')) ||
- (OPT_MINUS(ops,'X') && (OPT_ISSET(ops,'m') || *argv || !scriptname))) {
+ (OPT_MINUS(ops,'X') && (OPT_ISSET(ops,'m') || !scriptname))) {
zwarnnam(name, "invalid option(s)");
return 1;
}
@@ -3049,9 +3181,9 @@ bin_functions(char *name, char **argv, Options ops, int func)
} else if (OPT_ISSET(ops,'m')) {
/* List matching functions. */
for (; *argv; argv++) {
+ queue_signals();
tokenize(*argv);
if ((pprog = patcompile(*argv, PAT_STATIC, 0))) {
- queue_signals();
for (p = mathfuncs, q = NULL; p; q = p) {
MathFunc next;
do {
@@ -3070,12 +3202,12 @@ bin_functions(char *name, char **argv, Options ops, int func)
if (p)
p = p->next;
}
- unqueue_signals();
} else {
untokenize(*argv);
zwarnnam(name, "bad pattern : %s", *argv);
returnval = 1;
}
+ unqueue_signals();
}
} else if (OPT_PLUS(ops,'M')) {
/* Delete functions. -m is allowed but is handled above. */
@@ -3097,11 +3229,18 @@ bin_functions(char *name, char **argv, Options ops, int func)
}
} else {
/* Add a function */
- int minargs = 0, maxargs = -1;
+ int minargs, maxargs;
char *funcname = *argv++;
char *modname = NULL;
char *ptr;
+ if (OPT_ISSET(ops,'s')) {
+ minargs = maxargs = 1;
+ } else {
+ minargs = 0;
+ maxargs = -1;
+ }
+
ptr = itype_end(funcname, IIDENT, 0);
if (idigit(*funcname) || funcname == ptr || *ptr) {
zwarnnam(name, "-M %s: bad math function name", funcname);
@@ -3115,6 +3254,10 @@ bin_functions(char *name, char **argv, Options ops, int func)
*argv);
return 1;
}
+ if (OPT_ISSET(ops,'s') && minargs != 1) {
+ zwarnnam(name, "-Ms: must take a single string argument");
+ return 1;
+ }
maxargs = minargs;
argv++;
}
@@ -3128,6 +3271,10 @@ bin_functions(char *name, char **argv, Options ops, int func)
*argv);
return 1;
}
+ if (OPT_ISSET(ops,'s') && maxargs != 1) {
+ zwarnnam(name, "-Ms: must take a single string argument");
+ return 1;
+ }
argv++;
}
if (*argv)
@@ -3140,6 +3287,8 @@ bin_functions(char *name, char **argv, Options ops, int func)
p = (MathFunc)zshcalloc(sizeof(struct mathfunc));
p->name = ztrdup(funcname);
p->flags = MFF_USERFUNC;
+ if (OPT_ISSET(ops,'s'))
+ p->flags |= MFF_STR;
p->module = modname ? ztrdup(modname) : NULL;
p->minargs = minargs;
p->maxargs = maxargs;
@@ -3165,47 +3314,58 @@ bin_functions(char *name, char **argv, Options ops, int func)
return returnval;
}
- /* If no arguments given, we will print functions. If flags *
- * are given, we will print only functions containing these *
- * flags, else we'll print them all. */
- if (!*argv) {
- int ret = 0;
-
+ if (OPT_MINUS(ops,'X')) {
+ Funcstack fs;
+ char *funcname = NULL;
+ int ret;
+ if (*argv && argv[1]) {
+ zwarnnam(name, "-X: too many arguments");
+ return 1;
+ }
queue_signals();
- if (OPT_MINUS(ops,'X')) {
- Funcstack fs;
- char *funcname = NULL;
- for (fs = funcstack; fs; fs = fs->prev) {
- if (fs->tp == FS_FUNC) {
- /*
- * dupstring here is paranoia but unlikely to be
- * problematic
- */
- funcname = dupstring(fs->name);
- break;
- }
+ for (fs = funcstack; fs; fs = fs->prev) {
+ if (fs->tp == FS_FUNC) {
+ /*
+ * dupstring here is paranoia but unlikely to be
+ * problematic
+ */
+ funcname = dupstring(fs->name);
+ break;
}
- if (!funcname)
- {
- zerrnam(name, "bad autoload");
- ret = 1;
+ }
+ if (!funcname)
+ {
+ zerrnam(name, "bad autoload");
+ ret = 1;
+ } else {
+ if ((shf = (Shfunc) shfunctab->getnode(shfunctab, funcname))) {
+ DPUTS(!shf->funcdef,
+ "BUG: Calling autoload from empty function");
} else {
- if ((shf = (Shfunc) shfunctab->getnode(shfunctab, funcname))) {
- DPUTS(!shf->funcdef,
- "BUG: Calling autoload from empty function");
- } else {
- shf = (Shfunc) zshcalloc(sizeof *shf);
- shfunctab->addnode(shfunctab, ztrdup(funcname), shf);
- }
- shf->node.flags = on;
- ret = eval_autoload(shf, funcname, ops, func);
+ shf = (Shfunc) zshcalloc(sizeof *shf);
+ shfunctab->addnode(shfunctab, ztrdup(funcname), shf);
}
- } else {
- if (OPT_ISSET(ops,'U') && !OPT_ISSET(ops,'u'))
+ if (*argv) {
+ dircache_set(&shf->filename, NULL);
+ dircache_set(&shf->filename, *argv);
+ on |= PM_LOADDIR;
+ }
+ shf->node.flags = on;
+ ret = eval_autoload(shf, funcname, ops, func);
+ }
+ unqueue_signals();
+ return ret;
+ } else if (!*argv) {
+ /* If no arguments given, we will print functions. If flags *
+ * are given, we will print only functions containing these *
+ * flags, else we'll print them all. */
+ int ret = 0;
+
+ queue_signals();
+ if (OPT_ISSET(ops,'U') && !OPT_ISSET(ops,'u'))
on &= ~PM_UNDEFINED;
scanshfunc(1, on|off, DISABLED, shfunctab->printnode,
pflags, expand);
- }
unqueue_signals();
return ret;
}
@@ -3214,11 +3374,11 @@ bin_functions(char *name, char **argv, Options ops, int func)
if (OPT_ISSET(ops,'m')) {
on &= ~PM_UNDEFINED;
for (; *argv; argv++) {
+ queue_signals();
/* expand argument */
tokenize(*argv);
if ((pprog = patcompile(*argv, PAT_STATIC, 0))) {
/* with no options, just print all functions matching the glob pattern */
- queue_signals();
if (!(on|off) && !OPT_ISSET(ops,'X')) {
scanmatchshfunc(pprog, 1, 0, DISABLED,
shfunctab->printnode, pflags, expand);
@@ -3231,19 +3391,19 @@ bin_functions(char *name, char **argv, Options ops, int func)
!(shf->node.flags & DISABLED)) {
shf->node.flags = (shf->node.flags |
(on & ~PM_UNDEFINED)) & ~off;
- if (OPT_ISSET(ops,'X') &&
- eval_autoload(shf, shf->node.nam, ops, func)) {
+ if (check_autoload(shf, shf->node.nam,
+ ops, func)) {
returnval = 1;
}
}
}
}
- unqueue_signals();
} else {
untokenize(*argv);
zwarnnam(name, "bad pattern : %s", *argv);
returnval = 1;
}
+ unqueue_signals();
}
return returnval;
}
@@ -3258,8 +3418,7 @@ bin_functions(char *name, char **argv, Options ops, int func)
if (on|off) {
/* turn on/off the given flags */
shf->node.flags = (shf->node.flags | (on & ~PM_UNDEFINED)) & ~off;
- if (OPT_ISSET(ops,'X') &&
- eval_autoload(shf, shf->node.nam, ops, func))
+ if (check_autoload(shf, shf->node.nam, ops, func))
returnval = 1;
} else
/* no flags, so just print */
@@ -3276,13 +3435,38 @@ bin_functions(char *name, char **argv, Options ops, int func)
removetrapnode(signum);
}
+ if (**argv == '/') {
+ char *base = strrchr(*argv, '/') + 1;
+ if (*base &&
+ (shf = (Shfunc) shfunctab->getnode(shfunctab, base))) {
+ char *dir;
+ /* turn on/off the given flags */
+ shf->node.flags =
+ (shf->node.flags | (on & ~PM_UNDEFINED)) & ~off;
+ if (shf->node.flags & PM_UNDEFINED) {
+ /* update path if not yet loaded */
+ if (base == *argv + 1)
+ dir = "/";
+ else {
+ dir = *argv;
+ base[-1] = '\0';
+ }
+ dircache_set(&shf->filename, NULL);
+ dircache_set(&shf->filename, dir);
+ }
+ if (check_autoload(shf, shf->node.nam, ops, func))
+ returnval = 1;
+ continue;
+ }
+ }
+
/* Add a new undefined (autoloaded) function to the *
* hash table with the corresponding flags set. */
shf = (Shfunc) zshcalloc(sizeof *shf);
shf->node.flags = on;
shf->funcdef = mkautofn(shf);
shfunc_set_sticky(shf);
- shfunctab->addnode(shfunctab, ztrdup(*argv), shf);
+ add_autoload_function(shf, *argv);
if (signum != -1) {
if (settrap(signum, NULL, ZSIG_FUNC)) {
@@ -3293,8 +3477,7 @@ bin_functions(char *name, char **argv, Options ops, int func)
}
}
- if (ok && OPT_ISSET(ops,'X') &&
- eval_autoload(shf, shf->node.nam, ops, func))
+ if (ok && check_autoload(shf, shf->node.nam, ops, func))
returnval = 1;
} else
returnval = 1;
@@ -3348,11 +3531,11 @@ bin_unset(char *name, char **argv, Options ops, int func)
/* with -m option, treat arguments as glob patterns */
if (OPT_ISSET(ops,'m')) {
while ((s = *argv++)) {
+ queue_signals();
/* expand */
tokenize(s);
if ((pprog = patcompile(s, PAT_STATIC, NULL))) {
/* Go through the parameter table, and unset any matches */
- queue_signals();
for (i = 0; i < paramtab->hsize; i++) {
for (pm = (Param) paramtab->nodes[i]; pm; pm = next) {
/* record pointer to next, since we may free this one */
@@ -3365,12 +3548,12 @@ bin_unset(char *name, char **argv, Options ops, int func)
}
}
}
- unqueue_signals();
} else {
untokenize(s);
zwarnnam(name, "bad pattern : %s", s);
returnval = 1;
}
+ unqueue_signals();
}
/* If we didn't match anything, we return 1. */
if (!match)
@@ -3534,6 +3717,7 @@ bin_whence(char *nam, char **argv, Options ops, int func)
pushheap();
matchednodes = newlinklist();
}
+ queue_signals();
for (; *argv; argv++) {
/* parse the pattern */
tokenize(*argv);
@@ -3543,7 +3727,6 @@ bin_whence(char *nam, char **argv, Options ops, int func)
returnval = 1;
continue;
}
- queue_signals();
if (!OPT_ISSET(ops,'p')) {
/* -p option is for path search only. *
* We're not using it, so search for ... */
@@ -3574,9 +3757,9 @@ bin_whence(char *nam, char **argv, Options ops, int func)
scanmatchtable(cmdnamtab, pprog, 1, 0, 0,
(all ? fetchcmdnamnode : cmdnamtab->printnode),
printflags);
-
- unqueue_signals();
+ run_queued_signals();
}
+ unqueue_signals();
if (all) {
allmatched = argv = zlinklist2array(matchednodes);
matchednodes = NULL;
@@ -3653,9 +3836,11 @@ bin_whence(char *nam, char **argv, Options ops, int func)
if (wd) {
printf("%s: command\n", *argv);
} else {
- if (v && !csh)
+ if (v && !csh) {
zputs(*argv, stdout), fputs(" is ", stdout);
- zputs(buf, stdout);
+ quotedzputs(buf, stdout);
+ } else
+ zputs(buf, stdout);
if (OPT_ISSET(ops,'s') || OPT_ISSET(ops, 'S'))
print_if_link(buf, OPT_ISSET(ops, 'S'));
fputc('\n', stdout);
@@ -3685,9 +3870,11 @@ bin_whence(char *nam, char **argv, Options ops, int func)
if (wd) {
printf("%s: command\n", *argv);
} else {
- if (v && !csh)
+ if (v && !csh) {
zputs(*argv, stdout), fputs(" is ", stdout);
- zputs(cnam, stdout);
+ quotedzputs(cnam, stdout);
+ } else
+ zputs(cnam, stdout);
if (OPT_ISSET(ops,'s') || OPT_ISSET(ops,'S'))
print_if_link(cnam, OPT_ISSET(ops,'S'));
fputc('\n', stdout);
@@ -3899,11 +4086,11 @@ bin_unhash(char *name, char **argv, Options ops, int func)
* "unhash -m '*'" is legal, but not recommended. */
if (OPT_ISSET(ops,'m')) {
for (; *argv; argv++) {
+ queue_signals();
/* expand argument */
tokenize(*argv);
if ((pprog = patcompile(*argv, PAT_STATIC, NULL))) {
/* remove all nodes matching glob pattern */
- queue_signals();
for (i = 0; i < ht->hsize; i++) {
for (hn = ht->nodes[i]; hn; hn = nhn) {
/* record pointer to next, since we may free this one */
@@ -3914,12 +4101,12 @@ bin_unhash(char *name, char **argv, Options ops, int func)
}
}
}
- unqueue_signals();
} else {
untokenize(*argv);
zwarnnam(name, "bad pattern : %s", *argv);
returnval = 1;
}
+ unqueue_signals();
}
/* If we didn't match anything, we return 1. */
if (!match)
@@ -4002,18 +4189,18 @@ bin_alias(char *name, char **argv, Options ops, UNUSED(int func))
* glob patterns of aliases to display. */
if (OPT_ISSET(ops,'m')) {
for (; *argv; argv++) {
+ queue_signals();
tokenize(*argv); /* expand argument */
if ((pprog = patcompile(*argv, PAT_STATIC, NULL))) {
/* display the matching aliases */
- queue_signals();
scanmatchtable(ht, pprog, 1, flags1, flags2,
ht->printnode, printflags);
- unqueue_signals();
} else {
untokenize(*argv);
zwarnnam(name, "bad pattern : %s", *argv);
returnval = 1;
}
+ unqueue_signals();
}
return returnval;
}
@@ -4223,10 +4410,12 @@ bin_print(char *name, char **args, Options ops, int func)
zwarnnam(name, "no pattern specified");
return 1;
}
+ queue_signals();
tokenize(*args);
if (!(pprog = patcompile(*args, PAT_STATIC, NULL))) {
untokenize(*args);
zwarnnam(name, "bad pattern: %s", *args);
+ unqueue_signals();
return 1;
}
for (t = p = ++args; *p; p++)
@@ -4234,6 +4423,7 @@ bin_print(char *name, char **args, Options ops, int func)
*t++ = *p;
*t = NULL;
first = args;
+ unqueue_signals();
if (fmt && !*args) return 0;
}
/* compute lengths, and interpret according to -P, -D, -e, etc. */
@@ -5319,7 +5509,7 @@ bin_break(char *name, char **argv, UNUSED(Options ops), int func)
}
/*FALLTHROUGH*/
case BIN_EXIT:
- if (locallevel > forklevel) {
+ if (locallevel > forklevel && shell_exiting != -1) {
/*
* We don't exit directly from functions to allow tidying
* up, in particular EXIT traps. We still need to perform
@@ -5328,6 +5518,9 @@ bin_break(char *name, char **argv, UNUSED(Options ops), int func)
*
* If we are forked, we exit the shell at the function depth
* at which we became a subshell, hence the comparison.
+ *
+ * If we are already exiting... give this all up as
+ * a bad job.
*/
if (stopmsg || (zexit(0,2), !stopmsg)) {
retflag = 1;
@@ -5374,6 +5567,14 @@ checkjobs(void)
}
}
+/*
+ * -1 if the shell is already committed to exit.
+ * positive if zexit() was already called.
+ */
+
+/**/
+int shell_exiting;
+
/* exit the shell. val is the return value of the shell. *
* from_where is
* 1 if zexit is called because of a signal
@@ -5385,10 +5586,8 @@ checkjobs(void)
mod_export void
zexit(int val, int from_where)
{
- static int in_exit;
-
/* Don't do anything recursively: see below */
- if (in_exit == -1)
+ if (shell_exiting == -1)
return;
if (isset(MONITOR) && !stopmsg && from_where != 1) {
@@ -5401,14 +5600,14 @@ zexit(int val, int from_where)
}
}
/* Positive in_exit means we have been here before */
- if (from_where == 2 || (in_exit++ && from_where))
+ if (from_where == 2 || (shell_exiting++ && from_where))
return;
/*
- * We're now committed to exiting. Set in_exit to -1 to
+ * We're now committed to exiting. Set shell_exiting to -1 to
* indicate we shouldn't do any recursive processing.
*/
- in_exit = -1;
+ shell_exiting = -1;
/*
* We want to do all remaining processing regardless of preceding
* errors, even user interrupts.
diff --git a/Src/compat.c b/Src/compat.c
index a2956946f..a130d9264 100644
--- a/Src/compat.c
+++ b/Src/compat.c
@@ -672,7 +672,7 @@ strtoul(nptr, endptr, base)
/**/
int
-mk_wcwidth(wchar_t ucs)
+u9_wcwidth(wchar_t ucs)
{
int w = wcwidth9(ucs);
if (w < -1)
@@ -681,326 +681,16 @@ mk_wcwidth(wchar_t ucs)
}
/**/
-#elif defined(BROKEN_WCWIDTH) && (defined(__STDC_ISO_10646__) || defined(__APPLE__))
-
-/*
- * This is an implementation of wcwidth() and wcswidth() (defined in
- * IEEE Std 1002.1-2001) for Unicode.
- *
- * http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html
- * http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html
- *
- * In fixed-width output devices, Latin characters all occupy a single
- * "cell" position of equal width, whereas ideographic CJK characters
- * occupy two such cells. Interoperability between terminal-line
- * applications and (teletype-style) character terminals using the
- * UTF-8 encoding requires agreement on which character should advance
- * the cursor by how many cell positions. No established formal
- * standards exist at present on which Unicode character shall occupy
- * how many cell positions on character terminals. These routines are
- * a first attempt of defining such behavior based on simple rules
- * applied to data provided by the Unicode Consortium.
- *
- * For some graphical characters, the Unicode standard explicitly
- * defines a character-cell width via the definition of the East Asian
- * FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes.
- * In all these cases, there is no ambiguity about which width a
- * terminal shall use. For characters in the East Asian Ambiguous (A)
- * class, the width choice depends purely on a preference of backward
- * compatibility with either historic CJK or Western practice.
- * Choosing single-width for these characters is easy to justify as
- * the appropriate long-term solution, as the CJK practice of
- * displaying these characters as double-width comes from historic
- * implementation simplicity (8-bit encoded characters were displayed
- * single-width and 16-bit ones double-width, even for Greek,
- * Cyrillic, etc.) and not any typographic considerations.
- *
- * Much less clear is the choice of width for the Not East Asian
- * (Neutral) class. Existing practice does not dictate a width for any
- * of these characters. It would nevertheless make sense
- * typographically to allocate two character cells to characters such
- * as for instance EM SPACE or VOLUME INTEGRAL, which cannot be
- * represented adequately with a single-width glyph. The following
- * routines at present merely assign a single-cell width to all
- * neutral characters, in the interest of simplicity. This is not
- * entirely satisfactory and should be reconsidered before
- * establishing a formal standard in this area. At the moment, the
- * decision which Not East Asian (Neutral) characters should be
- * represented by double-width glyphs cannot yet be answered by
- * applying a simple rule from the Unicode database content. Setting
- * up a proper standard for the behavior of UTF-8 character terminals
- * will require a careful analysis not only of each Unicode character,
- * but also of each presentation form, something the author of these
- * routines has avoided to do so far.
- *
- * http://www.unicode.org/unicode/reports/tr11/
- *
- * Markus Kuhn -- 2007-05-26 (Unicode 5.0)
- *
- * Permission to use, copy, modify, and distribute this software
- * for any purpose and without fee is hereby granted. The author
- * disclaims all warranties with regard to this software.
- *
- * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
- */
-
-struct interval {
- int first;
- int last;
-};
-
-/* auxiliary function for binary search in interval table */
-static int bisearch(wchar_t ucs, const struct interval *table, int max) {
- int min = 0;
- int mid;
-
- if (ucs < table[0].first || ucs > table[max].last)
- return 0;
- while (max >= min) {
- mid = (min + max) / 2;
- if (ucs > table[mid].last)
- min = mid + 1;
- else if (ucs < table[mid].first)
- max = mid - 1;
- else
- return 1;
- }
-
- return 0;
-}
-
-
-/* The following two functions define the column width of an ISO 10646
- * character as follows:
- *
- * - The null character (U+0000) has a column width of 0.
- *
- * - Other C0/C1 control characters and DEL will lead to a return
- * value of -1.
- *
- * - Non-spacing and enclosing combining characters (general
- * category code Mn or Me in the Unicode database) have a
- * column width of 0.
- *
- * - SOFT HYPHEN (U+00AD) has a column width of 1.
- *
- * - Other format characters (general category code Cf in the Unicode
- * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0.
- *
- * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF)
- * have a column width of 0.
- *
- * - Spacing characters in the East Asian Wide (W) or East Asian
- * Full-width (F) category as defined in Unicode Technical
- * Report #11 have a column width of 2.
- *
- * - All remaining characters (including all printable
- * ISO 8859-1 and WGL4 characters, Unicode control characters,
- * etc.) have a column width of 1.
- *
- * This implementation assumes that wchar_t characters are encoded
- * in ISO 10646.
- */
-
-/**/
int
-mk_wcwidth(wchar_t ucs)
-{
- /* sorted list of non-overlapping intervals of non-spacing characters */
- /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */
- static const struct interval combining[] = {
- { 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 },
- { 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 },
- { 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 },
- { 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 },
- { 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED },
- { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A },
- { 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 },
- { 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D },
- { 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 },
- { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD },
- { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C },
- { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D },
- { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC },
- { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD },
- { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C },
- { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D },
- { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 },
- { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 },
- { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC },
- { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD },
- { 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D },
- { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 },
- { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E },
- { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC },
- { 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 },
- { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E },
- { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 },
- { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 },
- { 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 },
- { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F },
- { 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 },
- { 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD },
- { 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD },
- { 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 },
- { 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B },
- { 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 },
- { 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 },
- { 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF },
- { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 },
- { 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F },
- { 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B },
- { 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F },
- { 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB },
- { 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F },
- { 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 },
- { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD },
- { 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F },
- { 0xE0100, 0xE01EF }
- };
-
- /* test for 8-bit control characters */
- if (ucs == 0)
- return 0;
- if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0))
- return -1;
-
- /* binary search in table of non-spacing characters */
- if (bisearch(ucs, combining,
- sizeof(combining) / sizeof(struct interval) - 1))
- return 0;
-
- /* if we arrive here, ucs is not a combining or C0/C1 control character */
-
- return 1 +
- (ucs >= 0x1100 &&
- (ucs <= 0x115f || /* Hangul Jamo init. consonants */
- ucs == 0x2329 || ucs == 0x232a ||
- (ucs >= 0x2e80 && ucs <= 0xa4cf &&
- ucs != 0x303f) || /* CJK ... Yi */
- (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */
- (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */
- (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */
- (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */
- (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */
- (ucs >= 0xffe0 && ucs <= 0xffe6) ||
- (ucs >= 0x20000 && ucs <= 0x2fffd) ||
- (ucs >= 0x30000 && ucs <= 0x3fffd)));
-}
-
-
-/*
- * The following functions are part of the original wcwidth.c:
- * we don't use them but I've kept them in case - pws.
- */
-#if 0
-int mk_wcswidth(const wchar_t *pwcs, size_t n)
-{
- int w, width = 0;
-
- for (;*pwcs && n-- > 0; pwcs++)
- if ((w = mk_wcwidth(*pwcs)) < 0)
- return -1;
- else
- width += w;
-
- return width;
-}
-
-
-/*
- * The following functions are the same as mk_wcwidth() and
- * mk_wcswidth(), except that spacing characters in the East Asian
- * Ambiguous (A) category as defined in Unicode Technical Report #11
- * have a column width of 2. This variant might be useful for users of
- * CJK legacy encodings who want to migrate to UCS without changing
- * the traditional terminal character-width behaviour. It is not
- * otherwise recommended for general use.
- */
-int mk_wcwidth_cjk(wchar_t ucs)
+u9_iswprint(wint_t ucs)
{
- /* sorted list of non-overlapping intervals of East Asian Ambiguous
- * characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c" */
- static const struct interval ambiguous[] = {
- { 0x00A1, 0x00A1 }, { 0x00A4, 0x00A4 }, { 0x00A7, 0x00A8 },
- { 0x00AA, 0x00AA }, { 0x00AE, 0x00AE }, { 0x00B0, 0x00B4 },
- { 0x00B6, 0x00BA }, { 0x00BC, 0x00BF }, { 0x00C6, 0x00C6 },
- { 0x00D0, 0x00D0 }, { 0x00D7, 0x00D8 }, { 0x00DE, 0x00E1 },
- { 0x00E6, 0x00E6 }, { 0x00E8, 0x00EA }, { 0x00EC, 0x00ED },
- { 0x00F0, 0x00F0 }, { 0x00F2, 0x00F3 }, { 0x00F7, 0x00FA },
- { 0x00FC, 0x00FC }, { 0x00FE, 0x00FE }, { 0x0101, 0x0101 },
- { 0x0111, 0x0111 }, { 0x0113, 0x0113 }, { 0x011B, 0x011B },
- { 0x0126, 0x0127 }, { 0x012B, 0x012B }, { 0x0131, 0x0133 },
- { 0x0138, 0x0138 }, { 0x013F, 0x0142 }, { 0x0144, 0x0144 },
- { 0x0148, 0x014B }, { 0x014D, 0x014D }, { 0x0152, 0x0153 },
- { 0x0166, 0x0167 }, { 0x016B, 0x016B }, { 0x01CE, 0x01CE },
- { 0x01D0, 0x01D0 }, { 0x01D2, 0x01D2 }, { 0x01D4, 0x01D4 },
- { 0x01D6, 0x01D6 }, { 0x01D8, 0x01D8 }, { 0x01DA, 0x01DA },
- { 0x01DC, 0x01DC }, { 0x0251, 0x0251 }, { 0x0261, 0x0261 },
- { 0x02C4, 0x02C4 }, { 0x02C7, 0x02C7 }, { 0x02C9, 0x02CB },
- { 0x02CD, 0x02CD }, { 0x02D0, 0x02D0 }, { 0x02D8, 0x02DB },
- { 0x02DD, 0x02DD }, { 0x02DF, 0x02DF }, { 0x0391, 0x03A1 },
- { 0x03A3, 0x03A9 }, { 0x03B1, 0x03C1 }, { 0x03C3, 0x03C9 },
- { 0x0401, 0x0401 }, { 0x0410, 0x044F }, { 0x0451, 0x0451 },
- { 0x2010, 0x2010 }, { 0x2013, 0x2016 }, { 0x2018, 0x2019 },
- { 0x201C, 0x201D }, { 0x2020, 0x2022 }, { 0x2024, 0x2027 },
- { 0x2030, 0x2030 }, { 0x2032, 0x2033 }, { 0x2035, 0x2035 },
- { 0x203B, 0x203B }, { 0x203E, 0x203E }, { 0x2074, 0x2074 },
- { 0x207F, 0x207F }, { 0x2081, 0x2084 }, { 0x20AC, 0x20AC },
- { 0x2103, 0x2103 }, { 0x2105, 0x2105 }, { 0x2109, 0x2109 },
- { 0x2113, 0x2113 }, { 0x2116, 0x2116 }, { 0x2121, 0x2122 },
- { 0x2126, 0x2126 }, { 0x212B, 0x212B }, { 0x2153, 0x2154 },
- { 0x215B, 0x215E }, { 0x2160, 0x216B }, { 0x2170, 0x2179 },
- { 0x2190, 0x2199 }, { 0x21B8, 0x21B9 }, { 0x21D2, 0x21D2 },
- { 0x21D4, 0x21D4 }, { 0x21E7, 0x21E7 }, { 0x2200, 0x2200 },
- { 0x2202, 0x2203 }, { 0x2207, 0x2208 }, { 0x220B, 0x220B },
- { 0x220F, 0x220F }, { 0x2211, 0x2211 }, { 0x2215, 0x2215 },
- { 0x221A, 0x221A }, { 0x221D, 0x2220 }, { 0x2223, 0x2223 },
- { 0x2225, 0x2225 }, { 0x2227, 0x222C }, { 0x222E, 0x222E },
- { 0x2234, 0x2237 }, { 0x223C, 0x223D }, { 0x2248, 0x2248 },
- { 0x224C, 0x224C }, { 0x2252, 0x2252 }, { 0x2260, 0x2261 },
- { 0x2264, 0x2267 }, { 0x226A, 0x226B }, { 0x226E, 0x226F },
- { 0x2282, 0x2283 }, { 0x2286, 0x2287 }, { 0x2295, 0x2295 },
- { 0x2299, 0x2299 }, { 0x22A5, 0x22A5 }, { 0x22BF, 0x22BF },
- { 0x2312, 0x2312 }, { 0x2460, 0x24E9 }, { 0x24EB, 0x254B },
- { 0x2550, 0x2573 }, { 0x2580, 0x258F }, { 0x2592, 0x2595 },
- { 0x25A0, 0x25A1 }, { 0x25A3, 0x25A9 }, { 0x25B2, 0x25B3 },
- { 0x25B6, 0x25B7 }, { 0x25BC, 0x25BD }, { 0x25C0, 0x25C1 },
- { 0x25C6, 0x25C8 }, { 0x25CB, 0x25CB }, { 0x25CE, 0x25D1 },
- { 0x25E2, 0x25E5 }, { 0x25EF, 0x25EF }, { 0x2605, 0x2606 },
- { 0x2609, 0x2609 }, { 0x260E, 0x260F }, { 0x2614, 0x2615 },
- { 0x261C, 0x261C }, { 0x261E, 0x261E }, { 0x2640, 0x2640 },
- { 0x2642, 0x2642 }, { 0x2660, 0x2661 }, { 0x2663, 0x2665 },
- { 0x2667, 0x266A }, { 0x266C, 0x266D }, { 0x266F, 0x266F },
- { 0x273D, 0x273D }, { 0x2776, 0x277F }, { 0xE000, 0xF8FF },
- { 0xFFFD, 0xFFFD }, { 0xF0000, 0xFFFFD }, { 0x100000, 0x10FFFD }
- };
-
- /* binary search in table of non-spacing characters */
- if (bisearch(ucs, ambiguous,
- sizeof(ambiguous) / sizeof(struct interval) - 1))
- return 2;
-
- return mk_wcwidth(ucs);
+ if (ucs == 0)
+ return 0;
+ return wcwidth9(ucs) != -1;
}
-
-int mk_wcswidth_cjk(const wchar_t *pwcs, size_t n)
-{
- int w, width = 0;
-
- for (;*pwcs && n-- > 0; pwcs++)
- if ((w = mk_wcwidth_cjk(*pwcs)) < 0)
- return -1;
- else
- width += w;
-
- return width;
-}
-#endif /* 0 */
-
/**/
-#endif /* BROKEN_WCWIDTH && (__STDC_ISO_10646__ || __APPLE__) */
+#endif /* ENABLE_UNICODE9 */
/**/
#if defined(__APPLE__) && defined(BROKEN_ISPRINT)
diff --git a/Src/cond.c b/Src/cond.c
index 42e9de30f..b9a47cea5 100644
--- a/Src/cond.c
+++ b/Src/cond.c
@@ -138,13 +138,13 @@ evalcond(Estate state, char *fromtest)
strs = arrdup(sbuf);
l = 2;
}
- if (name && name[0] == '-')
- errname = name;
- else if (strs[0] && *strs[0] == '-')
- errname = strs[0];
+ if (name && IS_DASH(name[0]))
+ untokenize(errname = dupstring(name));
+ else if (strs[0] && IS_DASH(*strs[0]))
+ untokenize(errname = strs[0]);
else
errname = "<null>";
- if (name && name[0] == '-' &&
+ if (name && IS_DASH(name[0]) &&
(cd = getconddef((ctype == COND_MODI), name + 1, 1))) {
if (ctype == COND_MOD &&
(l < cd->min || (cd->max >= 0 && l > cd->max))) {
@@ -171,7 +171,7 @@ evalcond(Estate state, char *fromtest)
strs[0] = dupstring(name);
name = s;
- if (name && name[0] == '-' &&
+ if (name && IS_DASH(name[0]) &&
(cd = getconddef(0, name + 1, 1))) {
if (l < cd->min || (cd->max >= 0 && l > cd->max)) {
zwarnnam(fromtest, "unknown condition: %s",
@@ -295,6 +295,8 @@ evalcond(Estate state, char *fromtest)
int test, npat = state->pc[1];
Patprog pprog = state->prog->pats[npat];
+ queue_signals();
+
if (pprog == dummy_patprog1 || pprog == dummy_patprog2) {
char *opat;
int save;
@@ -308,6 +310,7 @@ evalcond(Estate state, char *fromtest)
if (!(pprog = patcompile(right, (save ? PAT_ZDUP : PAT_STATIC),
NULL))) {
zwarnnam(fromtest, "bad pattern: %s", right);
+ unqueue_signals();
return 2;
}
else if (save)
@@ -316,6 +319,8 @@ evalcond(Estate state, char *fromtest)
state->pc += 2;
test = (pprog && pattry(pprog, left));
+ unqueue_signals();
+
return !(ctype == COND_STRNEQ ? !test : test);
}
case COND_STRLT:
diff --git a/Src/exec.c b/Src/exec.c
index a439aec7f..f339dd6d0 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -975,9 +975,8 @@ entersubsh(int flags)
int sig, monitor, job_control_ok;
if (!(flags & ESUB_KEEPTRAP))
- for (sig = 0; sig < VSIGCOUNT; sig++)
- if (!(sigtrapped[sig] & ZSIG_FUNC) &&
- sig != SIGDEBUG && sig != SIGZERR)
+ for (sig = 0; sig < SIGCOUNT; sig++)
+ if (!(sigtrapped[sig] & ZSIG_FUNC))
unsettrap(sig);
monitor = isset(MONITOR);
job_control_ok = monitor && (flags & ESUB_JOB_CONTROL) && isset(POSIXJOBS);
@@ -1068,6 +1067,18 @@ entersubsh(int flags)
}
if (!(sigtrapped[SIGQUIT] & ZSIG_IGNORED))
signal_default(SIGQUIT);
+ /*
+ * sigtrapped[sig] == ZSIG_IGNORED for signals that remain ignored,
+ * but other trapped signals are temporarily blocked when intrap,
+ * and must be unblocked before continuing into the subshell. This
+ * is orthogonal to what the default handler for the signal may be.
+ *
+ * Start loop at 1 because 0 is SIGEXIT
+ */
+ if (intrap)
+ for (sig = 1; sig < SIGCOUNT; sig++)
+ if (sigtrapped[sig] && sigtrapped[sig] != ZSIG_IGNORED)
+ signal_unblock(signal_mask(sig));
if (!job_control_ok)
opts[MONITOR] = 0;
opts[USEZLE] = 0;
@@ -1601,6 +1612,7 @@ execpline(Estate state, wordcode slcode, int how, int last1)
zclose(opipe[0]);
}
if (how & Z_DISOWN) {
+ pipecleanfilelist(jobtab[thisjob].filelist, 0);
deletejob(jobtab + thisjob, 1);
thisjob = -1;
}
@@ -1848,7 +1860,7 @@ execpline2(Estate state, wordcode pcode,
lineno = WC_PIPE_LINENO(pcode) - 1;
if (pline_level == 1) {
- if ((how & Z_ASYNC) || (!sfcontext && !sourcelevel))
+ if ((how & Z_ASYNC) || !sfcontext)
strcpy(list_pipe_text,
getjobtext(state->prog,
state->pc + (WC_PIPE_TYPE(pcode) == WC_PIPE_END ?
@@ -2379,9 +2391,7 @@ addvars(Estate state, Wordcode pc, int addflags)
* to be restored after the command, since then the assignment
* is implicitly scoped.
*/
- flags = (!(addflags & ADDVAR_RESTORE) &&
- locallevel > forklevel && isset(WARNCREATEGLOBAL)) ?
- ASSPM_WARN_CREATE : 0;
+ flags = !(addflags & ADDVAR_RESTORE) ? ASSPM_WARN : 0;
xtr = isset(XTRACE);
if (xtr) {
printprompt4();
@@ -2633,6 +2643,27 @@ execcmd_analyse(Estate state, Execcmd_params eparams)
}
/*
+ * Transfer the first node of args to preargs, performing
+ * prefork expansion on the way if necessary.
+ */
+static void execcmd_getargs(LinkList preargs, LinkList args, int expand)
+{
+ if (!firstnode(args)) {
+ return;
+ } else if (expand) {
+ local_list0(svl);
+ init_list0(svl);
+ /* not init_list1, as we need real nodes */
+ addlinknode(&svl, uremnode(args, firstnode(args)));
+ /* Analysing commands, so vanilla options to prefork */
+ prefork(&svl, 0, NULL);
+ joinlists(preargs, &svl);
+ } else {
+ addlinknode(preargs, uremnode(args, firstnode(args)));
+ }
+}
+
+/*
* Execute a command at the lowest level of the hierarchy.
*/
@@ -2649,7 +2680,7 @@ execcmd_exec(Estate state, Execcmd_params eparams,
char *text;
int save[10];
int fil, dfil, is_cursh, do_exec = 0, redir_err = 0, i;
- int nullexec = 0, assign = 0, forked = 0;
+ int nullexec = 0, magic_assign = 0, forked = 0;
int is_shfunc = 0, is_builtin = 0, is_exec = 0, use_defpath = 0;
/* Various flags to the command. */
int cflags = 0, orig_cflags = 0, checked = 0, oautocont = -1;
@@ -2662,6 +2693,11 @@ execcmd_exec(Estate state, Execcmd_params eparams,
LinkList redir = eparams->redir;
Wordcode varspc = eparams->varspc;
int type = eparams->type;
+ /*
+ * preargs comes from expanding the head of the args list
+ * in order to check for prefix commands.
+ */
+ LinkList preargs;
doneps4 = 0;
@@ -2716,9 +2752,19 @@ execcmd_exec(Estate state, Execcmd_params eparams,
* command if it contains some tokens (e.g. x=ex; ${x}port), so this *
* only works in simple cases. has_token() is called to make sure *
* this really is a simple case. */
- if (type == WC_SIMPLE || type == WC_TYPESET) {
- while (args && nonempty(args)) {
- char *cmdarg = (char *) peekfirst(args);
+ if ((type == WC_SIMPLE || type == WC_TYPESET) && args) {
+ /*
+ * preargs contains args that have been expanded by prefork.
+ * Running execcmd_getargs() causes the any argument available
+ * in args to be exanded where necessary and transferred to
+ * preargs. We call execcmd_getargs() every time we need to
+ * analyse an argument not available in preargs, though there is
+ * no guarantee a further argument will be available.
+ */
+ preargs = newlinklist();
+ execcmd_getargs(preargs, args, eparams->htok);
+ while (nonempty(preargs)) {
+ char *cmdarg = (char *) peekfirst(preargs);
checked = !has_token(cmdarg);
if (!checked)
break;
@@ -2732,6 +2778,12 @@ execcmd_exec(Estate state, Execcmd_params eparams,
* Reserved words take precedence over shell functions.
*/
checked = 1;
+ } else if (isset(POSIXBUILTINS) && (cflags & BINF_EXEC)) {
+ /*
+ * POSIX doesn't allow "exec" to operate on builtins
+ * or shell functions.
+ */
+ break;
} else {
if (!(cflags & (BINF_BUILTIN | BINF_COMMAND)) &&
(hn = shfunctab->getnode(shfunctab, cmdarg))) {
@@ -2752,11 +2804,24 @@ execcmd_exec(Estate state, Execcmd_params eparams,
/* autoload the builtin if necessary */
if (!(hn = resolvebuiltin(cmdarg, hn)))
return;
- assign = (hn->flags & BINF_MAGICEQUALS);
+ if (type != WC_TYPESET)
+ magic_assign = (hn->flags & BINF_MAGICEQUALS);
break;
}
checked = 0;
- if ((cflags & BINF_COMMAND) && nextnode(firstnode(args))) {
+ /*
+ * We usually don't need the argument containing the
+ * precommand modifier itself. Exception: when "command"
+ * will implemented by a call to "whence", in which case
+ * we'll simply re-insert the argument.
+ */
+ uremnode(preargs, firstnode(preargs));
+ if (!firstnode(preargs)) {
+ execcmd_getargs(preargs, args, eparams->htok);
+ if (!firstnode(preargs))
+ break;
+ }
+ if ((cflags & BINF_COMMAND)) {
/*
* Check for options to "command".
* If just -p, this is handled here: use the default
@@ -2766,13 +2831,15 @@ execcmd_exec(Estate state, Execcmd_params eparams,
* Otherwise, just leave marked as BINF_COMMAND
* modifier with no additional action.
*/
- LinkNode argnode = nextnode(firstnode(args));
- char *argdata = (char *) getdata(argnode);
- char *cmdopt;
+ LinkNode argnode, oldnode, pnode = NULL;
+ char *argdata, *cmdopt;
int has_p = 0, has_vV = 0, has_other = 0;
- while (*argdata == '-') {
+ argnode = firstnode(preargs);
+ argdata = (char *) getdata(argnode);
+ while (IS_DASH(*argdata)) {
/* Just to be definite, stop on single "-", too, */
- if (!argdata[1] || (argdata[1] == '-' && !argdata[2]))
+ if (!argdata[1] ||
+ (IS_DASH(argdata[1]) && !argdata[2]))
break;
for (cmdopt = argdata+1; *cmdopt; cmdopt++) {
switch (*cmdopt) {
@@ -2785,6 +2852,7 @@ execcmd_exec(Estate state, Execcmd_params eparams,
* also traditional behaviour.
*/
has_p = 1;
+ pnode = argnode;
break;
case 'v':
case 'V':
@@ -2801,42 +2869,53 @@ execcmd_exec(Estate state, Execcmd_params eparams,
break;
}
+ oldnode = argnode;
argnode = nextnode(argnode);
- if (!argnode)
- break;
+ if (!argnode) {
+ execcmd_getargs(preargs, args, eparams->htok);
+ if (!(argnode = nextnode(oldnode)))
+ break;
+ }
argdata = (char *) getdata(argnode);
}
if (has_vV) {
- /* Leave everything alone, dispatch to whence */
+ /*
+ * Leave everything alone, dispatch to whence.
+ * We need to put the name back in the list.
+ */
+ pushnode(preargs, "command");
hn = &commandbn.node;
is_builtin = 1;
break;
} else if (has_p) {
- /* Use default path; absorb command and option. */
- uremnode(args, firstnode(args));
+ /* Use default path */
use_defpath = 1;
- if ((argnode = nextnode(firstnode(args))))
- argdata = (char *) getdata(argnode);
+ /*
+ * We don't need this node as we're not treating
+ * "command" as a builtin this time.
+ */
+ if (pnode)
+ uremnode(preargs, pnode);
}
/*
- * Else just absorb command and any trailing
+ * Else just any trailing
* end-of-options marker. This can only occur
* if we just had -p or something including more
* than just -p, -v and -V, in which case we behave
* as if this is command [non-option-stuff]. This
* isn't a good place for standard option handling.
*/
- if (!strcmp(argdata, "--"))
- uremnode(args, firstnode(args));
- }
- if ((cflags & BINF_EXEC) && nextnode(firstnode(args))) {
+ if (IS_DASH(argdata[0]) && IS_DASH(argdata[1]) && !argdata[2])
+ uremnode(preargs, argnode);
+ } else if (cflags & BINF_EXEC) {
/*
* Check for compatibility options to exec builtin.
* It would be nice to do these more generically,
* but currently we don't have a mechanism for
* precommand modifiers.
*/
- char *next = (char *) getdata(nextnode(firstnode(args)));
+ LinkNode argnode = firstnode(preargs), oldnode;
+ char *argdata = (char *) getdata(argnode);
char *cmdopt, *exec_argv0 = NULL;
/*
* Careful here: we want to make sure a final dash
@@ -2846,17 +2925,23 @@ execcmd_exec(Estate state, Execcmd_params eparams,
* people aren't likely to mix the option style
* with the zsh style.
*/
- while (next && *next == '-' && strlen(next) >= 2) {
- if (!firstnode(args)) {
+ while (argdata && IS_DASH(*argdata) && strlen(argdata) >= 2) {
+ oldnode = argnode;
+ argnode = nextnode(oldnode);
+ if (!argnode) {
+ execcmd_getargs(preargs, args, eparams->htok);
+ argnode = nextnode(oldnode);
+ }
+ if (!argnode) {
zerr("exec requires a command to execute");
lastval = 1;
errflag |= ERRFLAG_ERROR;
goto done;
}
- uremnode(args, firstnode(args));
- if (!strcmp(next, "--"))
+ uremnode(preargs, oldnode);
+ if (IS_DASH(argdata[0]) && IS_DASH(argdata[1]) && !argdata[2])
break;
- for (cmdopt = &next[1]; *cmdopt; ++cmdopt) {
+ for (cmdopt = &argdata[1]; *cmdopt; ++cmdopt) {
switch (*cmdopt) {
case 'a':
/* argument is ARGV0 string */
@@ -2865,21 +2950,25 @@ execcmd_exec(Estate state, Execcmd_params eparams,
/* position on last non-NULL character */
cmdopt += strlen(cmdopt+1);
} else {
- if (!firstnode(args)) {
+ if (!argnode) {
zerr("exec requires a command to execute");
lastval = 1;
errflag |= ERRFLAG_ERROR;
goto done;
}
- if (!nextnode(firstnode(args))) {
+ if (!nextnode(argnode))
+ execcmd_getargs(preargs, args,
+ eparams->htok);
+ if (!nextnode(argnode)) {
zerr("exec flag -a requires a parameter");
lastval = 1;
errflag |= ERRFLAG_ERROR;
goto done;
}
- exec_argv0 = (char *)
- getdata(nextnode(firstnode(args)));
- uremnode(args, firstnode(args));
+ exec_argv0 = (char *) getdata(argnode);
+ oldnode = argnode;
+ argnode = nextnode(argnode);
+ uremnode(args, oldnode);
}
break;
case 'c':
@@ -2895,8 +2984,9 @@ execcmd_exec(Estate state, Execcmd_params eparams,
return;
}
}
- if (firstnode(args) && nextnode(firstnode(args)))
- next = (char *) getdata(nextnode(firstnode(args)));
+ if (!argnode)
+ break;
+ argdata = (char *) getdata(argnode);
}
if (exec_argv0) {
char *str, *s;
@@ -2908,21 +2998,41 @@ execcmd_exec(Estate state, Execcmd_params eparams,
zputenv(str);
}
}
- uremnode(args, firstnode(args));
hn = NULL;
if ((cflags & BINF_COMMAND) && unset(POSIXBUILTINS))
break;
+ if (!nonempty(preargs))
+ execcmd_getargs(preargs, args, eparams->htok);
}
- }
+ } else
+ preargs = NULL;
/* if we get this far, it is OK to pay attention to lastval again */
if (noerrexit == 2 && !is_shfunc)
noerrexit = 0;
- /* Do prefork substitutions */
- esprefork = (assign || isset(MAGICEQUALSUBST)) ? PREFORK_TYPESET : 0;
- if (args && eparams->htok)
- prefork(args, esprefork, NULL);
+ /* Do prefork substitutions.
+ *
+ * Decide if we need "magic" handling of ~'s etc. in
+ * assignment-like arguments.
+ * - If magic_assign is set, we are using a builtin of the
+ * tyepset family, but did not recognise this as a keyword,
+ * so need guess-o-matic behaviour.
+ * - Otherwise, if we did recognise the keyword, we never need
+ * guess-o-matic behaviour as the argument was properly parsed
+ * as such.
+ * - Otherwise, use the behaviour specified by the MAGIC_EQUAL_SUBST
+ * option.
+ */
+ esprefork = (magic_assign ||
+ (isset(MAGICEQUALSUBST) && type != WC_TYPESET)) ?
+ PREFORK_TYPESET : 0;
+ if (args) {
+ if (eparams->htok)
+ prefork(args, esprefork, NULL);
+ if (preargs)
+ args = joinlists(preargs, args);
+ }
if (type == WC_SIMPLE || type == WC_TYPESET) {
int unglobbed = 0;
@@ -3019,10 +3129,14 @@ execcmd_exec(Estate state, Execcmd_params eparams,
* - we have determined there are options which would
* require us to use the "command" builtin); or
* - we aren't using POSIX and so BINF_COMMAND indicates a zsh
- * precommand modifier is being used in place of the builtin
+ * precommand modifier is being used in place of the
+ * builtin
+ * - we are using POSIX and this is an EXEC, so we can't
+ * execute a builtin or function.
*/
if (errflag || checked || is_builtin ||
- (unset(POSIXBUILTINS) && (cflags & BINF_COMMAND)))
+ (isset(POSIXBUILTINS) ?
+ (cflags & BINF_EXEC) : (cflags & BINF_COMMAND)))
break;
cmdarg = (char *) peekfirst(args);
@@ -3065,7 +3179,7 @@ execcmd_exec(Estate state, Execcmd_params eparams,
/* Get the text associated with this command. */
if ((how & Z_ASYNC) ||
- (!sfcontext && !sourcelevel && (jobbing || (how & Z_TIMED))))
+ (!sfcontext && (jobbing || (how & Z_TIMED))))
text = getjobtext(state->prog, eparams->beg);
else
text = NULL;
@@ -3124,7 +3238,7 @@ execcmd_exec(Estate state, Execcmd_params eparams,
if (is_shfunc)
shf = (Shfunc)hn;
else {
- shf = loadautofn(state->prog->shf, 1, 0);
+ shf = loadautofn(state->prog->shf, 1, 0, 0);
if (shf)
state->prog->shf = shf;
else {
@@ -3700,7 +3814,7 @@ execcmd_exec(Estate state, Execcmd_params eparams,
* Save if it's got "command" in front or it's
* not a magic-equals assignment.
*/
- if ((cflags & (BINF_COMMAND|BINF_ASSIGN)) || !assign)
+ if ((cflags & (BINF_COMMAND|BINF_ASSIGN)) || !magic_assign)
do_save = 1;
}
if (do_save && varspc)
@@ -3987,6 +4101,7 @@ execcmd_exec(Estate state, Execcmd_params eparams,
* classify as a builtin) we treat all errors as fatal.
* The "command" builtin is not special so resets this behaviour.
*/
+ forked |= zsh_subshell;
fatal:
if (redir_err || errflag) {
if (!isset(INTERACTIVE)) {
@@ -4465,7 +4580,7 @@ getoutputfile(char *cmd, char **eptr)
}
if (!(prog = parsecmd(cmd, eptr)))
return NULL;
- if (!(nam = gettempname(NULL, 0)))
+ if (!(nam = gettempname(NULL, 1)))
return NULL;
if ((s = simple_redir_name(prog, REDIR_HERESTR))) {
@@ -4496,7 +4611,7 @@ getoutputfile(char *cmd, char **eptr)
suffix = dyncat(nam, unmeta(suffix));
if (link(nam, suffix) == 0) {
addfilelist(nam, 0);
- nam = ztrdup(suffix);
+ nam = suffix;
}
}
}
@@ -4902,6 +5017,7 @@ execfuncdef(Estate state, Eprog redir_prog)
shf = (Shfunc) zalloc(sizeof(*shf));
shf->funcdef = prog;
shf->node.flags = 0;
+ /* No dircache here, not a directory */
shf->filename = ztrdup(scriptfilename);
shf->lineno = lineno;
/*
@@ -4934,7 +5050,7 @@ execfuncdef(Estate state, Eprog redir_prog)
freeeprog(shf->funcdef);
if (shf->redir) /* shouldn't be */
freeeprog(shf->redir);
- zsfree(shf->filename);
+ dircache_set(&shf->filename, NULL);
zfree(shf, sizeof(*shf));
state->pc = end;
return 1;
@@ -4965,7 +5081,7 @@ execfuncdef(Estate state, Eprog redir_prog)
freeeprog(shf->funcdef);
if (shf->redir) /* shouldn't be */
freeeprog(shf->redir);
- zsfree(shf->filename);
+ dircache_set(&shf->filename, NULL);
zfree(shf, sizeof(*shf));
break;
} else {
@@ -4974,7 +5090,7 @@ execfuncdef(Estate state, Eprog redir_prog)
(signum = getsignum(s + 4)) != -1) {
if (settrap(signum, NULL, ZSIG_FUNC)) {
freeeprog(shf->funcdef);
- zsfree(shf->filename);
+ dircache_set(&shf->filename, NULL);
zfree(shf, sizeof(*shf));
state->pc = end;
return 1;
@@ -5123,12 +5239,12 @@ execautofn_basic(Estate state, UNUSED(int do_exec))
* defined yet.
*/
if (funcstack && !funcstack->filename)
- funcstack->filename = dupstring(shf->filename);
+ funcstack->filename = getshfuncfile(shf);
oldscriptname = scriptname;
oldscriptfilename = scriptfilename;
scriptname = dupstring(shf->node.nam);
- scriptfilename = dupstring(shf->filename);
+ scriptfilename = getshfuncfile(shf);
execode(shf->funcdef, 1, 0, "loadautofunc");
scriptname = oldscriptname;
scriptfilename = oldscriptfilename;
@@ -5142,25 +5258,71 @@ execautofn(Estate state, UNUSED(int do_exec))
{
Shfunc shf;
- if (!(shf = loadautofn(state->prog->shf, 1, 0)))
+ if (!(shf = loadautofn(state->prog->shf, 1, 0, 0)))
return 1;
state->prog->shf = shf;
return execautofn_basic(state, 0);
}
+/*
+ * Helper function to install the source file name of a shell function
+ * just autoloaded.
+ *
+ * We attempt to do this efficiently as the typical case is the
+ * directory part is a well-known directory, which is cached, and
+ * the non-directory part is the same as the node name.
+ */
+
+/**/
+static void
+loadautofnsetfile(Shfunc shf, char *fdir)
+{
+ /*
+ * If shf->filename is already the load directory ---
+ * keep it as we can still use it to get the load file.
+ * This makes autoload with an absolute path particularly efficient.
+ */
+ if (!(shf->node.flags & PM_LOADDIR) ||
+ strcmp(shf->filename, fdir) != 0) {
+ /* Old directory name not useful... */
+ dircache_set(&shf->filename, NULL);
+ if (fdir) {
+ /* ...can still cache directory */
+ shf->node.flags |= PM_LOADDIR;
+ dircache_set(&shf->filename, fdir);
+ } else {
+ /* ...no separate directory part to cache, for some reason. */
+ shf->node.flags &= ~PM_LOADDIR;
+ shf->filename = ztrdup(shf->node.nam);
+ }
+ }
+}
+
/**/
Shfunc
-loadautofn(Shfunc shf, int fksh, int autol)
+loadautofn(Shfunc shf, int fksh, int autol, int current_fpath)
{
int noalias = noaliases, ksh = 1;
Eprog prog;
- char *fname;
+ char *fdir; /* Directory path where func found */
pushheap();
noaliases = (shf->node.flags & PM_UNALIASED);
- prog = getfpfunc(shf->node.nam, &ksh, &fname);
+ if (shf->filename && shf->filename[0] == '/' &&
+ (shf->node.flags & PM_LOADDIR))
+ {
+ char *spec_path[2];
+ spec_path[0] = dupstring(shf->filename);
+ spec_path[1] = NULL;
+ prog = getfpfunc(shf->node.nam, &ksh, &fdir, spec_path, 0);
+ if (prog == &dummy_eprog &&
+ (current_fpath || (shf->node.flags & PM_CUR_FPATH)))
+ prog = getfpfunc(shf->node.nam, &ksh, &fdir, NULL, 0);
+ }
+ else
+ prog = getfpfunc(shf->node.nam, &ksh, &fdir, NULL, 0);
noaliases = noalias;
if (ksh == 1) {
@@ -5179,7 +5341,6 @@ loadautofn(Shfunc shf, int fksh, int autol)
return NULL;
}
if (!prog) {
- zsfree(fname);
popheap();
return NULL;
}
@@ -5193,7 +5354,7 @@ loadautofn(Shfunc shf, int fksh, int autol)
else
shf->funcdef = dupeprog(prog, 0);
shf->node.flags &= ~PM_UNDEFINED;
- shf->filename = fname;
+ loadautofnsetfile(shf, fdir);
} else {
VARARR(char, n, strlen(shf->node.nam) + 1);
strcpy(n, shf->node.nam);
@@ -5205,7 +5366,6 @@ loadautofn(Shfunc shf, int fksh, int autol)
zwarn("%s: function not defined by file", n);
locallevel++;
popheap();
- zsfree(fname);
return NULL;
}
}
@@ -5216,7 +5376,7 @@ loadautofn(Shfunc shf, int fksh, int autol)
else
shf->funcdef = dupeprog(stripkshdef(prog, shf->node.nam), 0);
shf->node.flags &= ~PM_UNDEFINED;
- shf->filename = fname;
+ loadautofnsetfile(shf, fdir);
}
popheap();
@@ -5386,6 +5546,14 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
else
opts[XTRACE] = 0;
}
+ if (flags & PM_WARNNESTED)
+ opts[WARNNESTEDVAR] = 1;
+ else if (oflags & PM_WARNNESTED) {
+ if (shfunc->node.nam == ANONYMOUS_FUNCTION_NAME)
+ flags |= PM_WARNNESTED;
+ else
+ opts[WARNNESTEDVAR] = 0;
+ }
ooflags = oflags;
/*
* oflags is static, because we compare it on the next recursive
@@ -5436,7 +5604,7 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
funcstack = &fstack;
fstack.flineno = shfunc->lineno;
- fstack.filename = dupstring(shfunc->filename);
+ fstack.filename = getshfuncfile(shfunc);
prog = shfunc->funcdef;
if (prog->flags & EF_RUN) {
@@ -5504,6 +5672,7 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
opts[PRINTEXITVALUE] = saveopts[PRINTEXITVALUE];
opts[LOCALOPTIONS] = saveopts[LOCALOPTIONS];
opts[LOCALLOOPS] = saveopts[LOCALLOOPS];
+ opts[WARNNESTEDVAR] = saveopts[WARNNESTEDVAR];
}
if (opts[LOCALLOOPS]) {
@@ -5537,8 +5706,11 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
* the only likely case where we need that second test is
* when we have an "always" block. The endparamscope() has
* already happened, hence the "+1" here.
+ *
+ * If we are in an exit trap, finish it first... we wouldn't set
+ * exit_pending if we were already in one.
*/
- if (exit_pending && exit_level >= locallevel+1) {
+ if (exit_pending && exit_level >= locallevel+1 && !in_exit_trap) {
if (locallevel > forklevel) {
/* Still functions to return: force them to do so. */
retflag = 1;
@@ -5602,12 +5774,21 @@ runshfunc(Eprog prog, FuncWrap wrap, char *name)
unqueue_signals();
}
-/* Search fpath for an undefined function. Finds the file, and returns the *
- * list of its contents. */
+/*
+ * Search fpath for an undefined function. Finds the file, and returns the
+ * list of its contents.
+ *
+ * If test is 0, load the function.
+ *
+ * If test_only is 1, don't load function, just test for it:
+ * Non-null return means function was found
+ *
+ * *fdir points to path at which found (as passed in, not duplicated)
+ */
/**/
Eprog
-getfpfunc(char *s, int *ksh, char **fname)
+getfpfunc(char *s, int *ksh, char **fdir, char **alt_path, int test_only)
{
char **pp, buf[PATH_MAX+1];
off_t len;
@@ -5616,7 +5797,7 @@ getfpfunc(char *s, int *ksh, char **fname)
Eprog r;
int fd;
- pp = fpath;
+ pp = alt_path ? alt_path : fpath;
for (; *pp; pp++) {
if (strlen(*pp) + strlen(s) + 1 >= PATH_MAX)
continue;
@@ -5624,9 +5805,9 @@ getfpfunc(char *s, int *ksh, char **fname)
sprintf(buf, "%s/%s", *pp, s);
else
strcpy(buf, s);
- if ((r = try_dump_file(*pp, s, buf, ksh))) {
- if (fname)
- *fname = ztrdup(buf);
+ if ((r = try_dump_file(*pp, s, buf, ksh, test_only))) {
+ if (fdir)
+ *fdir = *pp;
return r;
}
unmetafy(buf, NULL);
@@ -5634,6 +5815,12 @@ getfpfunc(char *s, int *ksh, char **fname)
struct stat st;
if (!fstat(fd, &st) && S_ISREG(st.st_mode) &&
(len = lseek(fd, 0, 2)) != -1) {
+ if (test_only) {
+ close(fd);
+ if (fdir)
+ *fdir = *pp;
+ return &dummy_eprog;
+ }
d = (char *) zalloc(len + 1);
lseek(fd, 0, 0);
if ((rlen = read(fd, d, len)) >= 0) {
@@ -5647,8 +5834,8 @@ getfpfunc(char *s, int *ksh, char **fname)
r = parse_string(d, 1);
scriptname = oldscriptname;
- if (fname)
- *fname = ztrdup(buf);
+ if (fdir)
+ *fdir = *pp;
zfree(d, len + 1);
@@ -5661,7 +5848,7 @@ getfpfunc(char *s, int *ksh, char **fname)
close(fd);
}
}
- return &dummy_eprog;
+ return test_only ? NULL : &dummy_eprog;
}
/* Handle the most common type of ksh-style autoloading, when doing a *
diff --git a/Src/glob.c b/Src/glob.c
index 623e6f1d6..c9ec97e0e 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -1314,6 +1314,7 @@ zglob(LinkList list, LinkNode np, int nountok)
sense ^= 1;
break;
case '-':
+ case Dash:
/* Toggle matching of symbolic links */
sense ^= 2;
break;
@@ -1608,7 +1609,7 @@ zglob(LinkList list, LinkNode np, int nountok)
++s;
}
/* See if it's greater than, equal to, or less than */
- if ((g_range = *s == '+' ? 1 : *s == '-' ? -1 : 0))
+ if ((g_range = *s == '+' ? 1 : IS_DASH(*s) ? -1 : 0))
++s;
data = qgetnum(&s);
break;
@@ -2025,13 +2026,13 @@ hasbraces(char *str)
if (bracechardots(str-1, NULL, NULL))
return 1;
lbr = str - 1;
- if (*str == '-')
+ if (IS_DASH(*str))
str++;
while (idigit(*str))
str++;
if (*str == '.' && str[1] == '.') {
str++; str++;
- if (*str == '-')
+ if (IS_DASH(*str))
str++;
while (idigit(*str))
str++;
@@ -2040,7 +2041,7 @@ hasbraces(char *str)
return 1;
else if (*str == '.' && str[1] == '.') {
str++; str++;
- if (*str == '-')
+ if (IS_DASH(*str))
str++;
while (idigit(*str))
str++;
@@ -2123,7 +2124,7 @@ xpandredir(struct redir *fn, LinkList redirtab)
fn->name = s;
untokenize(s);
if (fn->type == REDIR_MERGEIN || fn->type == REDIR_MERGEOUT) {
- if (s[0] == '-' && !s[1])
+ if (IS_DASH(s[0]) && !s[1])
fn->type = REDIR_CLOSE;
else if (s[0] == 'p' && !s[1])
fn->fd2 = -2;
@@ -2193,6 +2194,8 @@ bracechardots(char *str, convchar_t *c1p, convchar_t *c2p)
pnext[0] != '.' || pnext[1] != '.')
return 0;
pnext += 2;
+ if (!*pnext)
+ return 0;
if (itok(*pnext)) {
if (*pnext == Inbrace)
return 0;
@@ -2329,12 +2332,14 @@ xpandbraces(LinkList list, LinkNode *np)
* str+1 is the first number in the range, dots+2 the last,
* and dots2+2 is the increment if that's given. */
/* TODO: sorry about this */
- int minw = (str[1] == '0' || (str[1] == '-' && str[2] == '0'))
+ int minw = (str[1] == '0' ||
+ (IS_DASH(str[1]) && str[2] == '0'))
? wid1
- : (dots[2] == '0' || (dots[2] == '-' && dots[3] == '0'))
+ : (dots[2] == '0' ||
+ (IS_DASH(dots[2]) && dots[3] == '0'))
? wid2
: (dots2 && (dots2[2] == '0' ||
- (dots2[2] == '-' && dots2[3] == '0')))
+ (IS_DASH(dots2[2]) && dots2[3] == '0')))
? wid3
: 0;
if (rincr < 0) {
@@ -2392,7 +2397,8 @@ xpandbraces(LinkList list, LinkNode *np)
c2 = ztokens[c2 - STOUC(Pound)];
if ((char) c2 == Meta)
c2 = 32 ^ p[1];
- if (c1 == '-' && lastch >= 0 && p < str2 && lastch <= (int)c2) {
+ if (IS_DASH((char)c1) && lastch >= 0 &&
+ p < str2 && lastch <= (int)c2) {
while (lastch < (int)c2)
ccl[lastch++] = 1;
lastch = -1;
@@ -2462,13 +2468,20 @@ xpandbraces(LinkList list, LinkNode *np)
int
matchpat(char *a, char *b)
{
- Patprog p = patcompile(b, PAT_STATIC, NULL);
+ Patprog p;
+ int ret;
- if (!p) {
+ queue_signals(); /* Protect PAT_STATIC */
+
+ if (!(p = patcompile(b, PAT_STATIC, NULL))) {
zerr("bad pattern: %s", b);
- return 0;
- }
- return pattry(p, a);
+ ret = 0;
+ } else
+ ret = pattry(p, a);
+
+ unqueue_signals();
+
+ return ret;
}
/* do the ${foo%%bar}, ${foo#bar} stuff */
@@ -2918,7 +2931,7 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr,
* move forward along string until we get a match. *
* Again there's no optimisation. */
mb_charinit();
- for (ioff = 0, t = s, umlen = umltot; t < send ; ioff++) {
+ for (ioff = 0, t = s, umlen = umltot; t <= send ; ioff++) {
set_pat_start(p, t-s);
if (pattrylen(p, t, umlen, 0, &patstralloc, ioff)) {
*sp = get_match_ret(&imd, t-s, umltot);
@@ -2926,6 +2939,8 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr,
}
if (fl & SUB_START)
break;
+ if (t == send)
+ break;
umlen -= iincchar(&t, send - t);
}
if (!(fl & SUB_START) && pattrylen(p, send, 0, 0,
@@ -2958,7 +2973,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; ioff++) {
+ for (; t <= send; ioff++) {
/* Find the longest match from this position. */
set_pat_start(p, t-s);
if (pattrylen(p, t, umlen, 0, &patstralloc, ioff)) {
@@ -3007,15 +3022,19 @@ 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 -= iincchar(&t, send - t);
}
break;
}
+ if (t == send)
+ break;
umlen -= iincchar(&t, send - t);
}
- } 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
@@ -3521,7 +3540,7 @@ zshtokenize(char *s, int flags)
}
t = s;
while (idigit(*++s));
- if (*s != '-')
+ if (!IS_DASH(*s))
goto cont;
while (idigit(*++s));
if (*s != '>')
diff --git a/Src/hashtable.c b/Src/hashtable.c
index 7c3367568..6ec2ed220 100644
--- a/Src/hashtable.c
+++ b/Src/hashtable.c
@@ -889,7 +889,7 @@ freeshfuncnode(HashNode hn)
freeeprog(shf->funcdef);
if (shf->redir)
freeeprog(shf->redir);
- zsfree(shf->filename);
+ dircache_set(&shf->filename, NULL);
if (shf->sticky) {
if (shf->sticky->n_on_opts)
zfree(shf->sticky->on_opts,
@@ -926,10 +926,13 @@ printshfuncnode(HashNode hn, int printflags)
(f->node.flags & PM_UNDEFINED) ?
" is an autoload shell function" :
" is a shell function");
- if (f->filename && (printflags & PRINT_WHENCE_VERBOSE) &&
- strcmp(f->filename, f->node.nam) != 0) {
+ if ((printflags & PRINT_WHENCE_VERBOSE) && f->filename) {
printf(" from ");
quotedzputs(f->filename, stdout);
+ if (f->node.flags & PM_LOADDIR) {
+ printf("/");
+ quotedzputs(f->node.nam, stdout);
+ }
}
putchar('\n');
return;
@@ -949,16 +952,20 @@ printshfuncnode(HashNode hn, int printflags)
zoutputtab(stdout);
}
if (!t) {
- char *fopt = "UtTkz";
+ char *fopt = "UtTkzc";
int flgs[] = {
PM_UNALIASED, PM_TAGGED, PM_TAGGED_LOCAL,
- PM_KSHSTORED, PM_ZSHSTORED, 0
+ PM_KSHSTORED, PM_ZSHSTORED, PM_CUR_FPATH, 0
};
int fl;;
zputs("builtin autoload -X", stdout);
for (fl=0;fopt[fl];fl++)
if (f->node.flags & flgs[fl]) putchar(fopt[fl]);
+ if (f->filename && (f->node.flags & PM_LOADDIR)) {
+ putchar(' ');
+ zputs(f->filename, stdout);
+ }
} else {
zputs(t, stdout);
zsfree(t);
@@ -1037,6 +1044,24 @@ printshfuncexpand(HashNode hn, int printflags, int expand)
text_expand_tabs = save_expand;
}
+/*
+ * Get a heap-duplicated name of the shell function, for
+ * use in tracing.
+ */
+
+/**/
+mod_export char *
+getshfuncfile(Shfunc shf)
+{
+ if (shf->node.flags & PM_LOADDIR) {
+ return zhtricat(shf->filename, "/", shf->node.nam);
+ } else if (shf->filename) {
+ return dupstring(shf->filename);
+ } else {
+ return NULL;
+ }
+}
+
/**************************************/
/* Reserved Word Hash Table Functions */
/**************************************/
@@ -1420,6 +1445,9 @@ freehistdata(Histent he, int unlink)
if (!he)
return;
+ if (he == &curline)
+ return;
+
if (!(he->node.flags & (HIST_DUP | HIST_TMPSTORE)))
removehashnode(histtab, he->node.nam);
@@ -1438,3 +1466,150 @@ freehistdata(Histent he, int unlink)
}
}
}
+
+
+/***********************************************************************
+ * Directory name cache mechanism
+ *
+ * The idea of this is that there are various shell structures,
+ * notably functions, that record the directories with which they
+ * are associated. Rather than store the full string each time,
+ * we store a pointer to the same location and count the references.
+ * This is optimised so that retrieval is quick at the expense of
+ * searching the list when setting up the structure, which is a much
+ * rarer operation.
+ *
+ * There is nothing special about the fact that the strings are
+ * directories, except for the assumptions for efficiency that many
+ * structures will point to the same one, and that there are not too
+ * many different directories associated with the shell.
+ **********************************************************************/
+
+struct dircache_entry
+{
+ /* Name of directory in cache */
+ char *name;
+ /* Number of references to it */
+ int refs;
+};
+
+/*
+ * dircache is the cache, of length dircache_size.
+ * dircache_lastentry is the last entry used, an optimisation
+ * for multiple references to the same directory, e.g
+ * "autoload /blah/blah/\*".
+ */
+static struct dircache_entry *dircache, *dircache_lastentry;
+static int dircache_size;
+
+/*
+ * Set *name to point to a cached version of value.
+ * value is copied so may come from any source.
+ *
+ * If value is NULL, look for the existing value of *name (safe if this
+ * too is NULL) and remove a reference to it from the cache. If it's
+ * not found in the cache, it's assumed to be an allocated string and
+ * freed --- this currently occurs for a shell function that's been
+ * loaded as the filename is now a full path, not just a directory,
+ * though we may one day optimise this to a cached directory plus a
+ * name, too. Note --- the function does *not* otherwise check
+ * if *name points to something already cached, so this is
+ * necessary any time *name may already be in the cache.
+ */
+
+/**/
+mod_export void
+dircache_set(char **name, char *value)
+{
+ struct dircache_entry *dcptr, *dcnew;
+
+ if (!value) {
+ if (!*name)
+ return;
+ if (!dircache_size) {
+ zsfree(*name);
+ *name = NULL;
+ return;
+ }
+
+ for (dcptr = dircache; dcptr < dircache + dircache_size; dcptr++)
+ {
+ /* Must be a pointer much, not a string match */
+ if (*name == dcptr->name)
+ {
+ --dcptr->refs;
+ if (!dcptr->refs) {
+ ptrdiff_t ind = dcptr - dircache;
+ zsfree(dcptr->name);
+ --dircache_size;
+
+ if (!dircache_size) {
+ zfree(dircache, sizeof(*dircache));
+ dircache = NULL;
+ dircache_lastentry = NULL;
+ *name = NULL;
+ return;
+ }
+ dcnew = (struct dircache_entry *)
+ zalloc(dircache_size * sizeof(*dcnew));
+ if (ind)
+ memcpy(dcnew, dircache, ind * sizeof(*dcnew));
+ if (ind < dircache_size)
+ memcpy(dcnew + ind, dcptr + 1,
+ (dircache_size - ind) * sizeof(*dcnew));
+ zfree(dircache, (dircache_size+1)*sizeof(*dcnew));
+ dircache = dcnew;
+ dircache_lastentry = NULL;
+ }
+ *name = NULL;
+ return;
+ }
+ }
+ zsfree(*name);
+ *name = NULL;
+ } else {
+ /*
+ * As the function path has been resolved to a particular
+ * location, we'll store it as an absolute path.
+ */
+ if (*value != '/') {
+ value = zhtricat(metafy(zgetcwd(), -1, META_HEAPDUP),
+ "/", value);
+ value = xsymlink(value, 1);
+ }
+ /*
+ * We'll maintain the cache at exactly the right size rather
+ * than overallocating. The rationale here is that typically
+ * we'll get a lot of functions in a small number of directories
+ * so the complexity overhead of maintaining a separate count
+ * isn't really matched by the efficiency gain.
+ */
+ if (dircache_lastentry &&
+ !strcmp(value, dircache_lastentry->name)) {
+ *name = dircache_lastentry->name;
+ ++dircache_lastentry->refs;
+ return;
+ } else if (!dircache_size) {
+ dircache_size = 1;
+ dcptr = dircache =
+ (struct dircache_entry *)zalloc(sizeof(*dircache));
+ } else {
+ for (dcptr = dircache; dcptr < dircache + dircache_size; dcptr++)
+ {
+ if (!strcmp(value, dcptr->name)) {
+ *name = dcptr->name;
+ ++dcptr->refs;
+ return;
+ }
+ }
+ ++dircache_size;
+ dircache = (struct dircache_entry *)
+ zrealloc(dircache, sizeof(*dircache) * dircache_size);
+ dcptr = dircache + dircache_size - 1;
+ }
+ dcptr->name = ztrdup(value);
+ *name = dcptr->name;
+ dcptr->refs = 1;
+ dircache_lastentry = dcptr;
+ }
+}
diff --git a/Src/hist.c b/Src/hist.c
index 97fd34039..da5a8b29f 100644
--- a/Src/hist.c
+++ b/Src/hist.c
@@ -253,6 +253,7 @@ hist_context_save(struct hist_stack *hs, int toplevel)
hs->hwend = hwend;
hs->addtoline = addtoline;
hs->hlinesz = hlinesz;
+ hs->defev = defev;
/*
* We save and restore the command stack with history
* as it's visible to the user interactively, so if
@@ -296,6 +297,7 @@ hist_context_restore(const struct hist_stack *hs, int toplevel)
hwend = hs->hwend;
addtoline = hs->addtoline;
hlinesz = hs->hlinesz;
+ defev = hs->defev;
if (cmdstack)
zfree(cmdstack, CMDSTACKSZ);
cmdstack = hs->cstack;
@@ -1418,7 +1420,7 @@ hend(Eprog prog)
DPUTS(hptr < chline, "History end pointer off start of line");
*hptr = '\0';
}
- {
+ if (*chline) {
LinkList hookargs = newlinklist();
int save_errflag = errflag;
errflag = 0;
@@ -1427,6 +1429,7 @@ hend(Eprog prog)
addlinknode(hookargs, chline);
callhookfunc("zshaddhistory", hookargs, 1, &hookret);
+ errflag &= ~ERRFLAG_ERROR;
errflag |= save_errflag;
}
/* For history sharing, lock history file once for both read and write */
diff --git a/Src/init.c b/Src/init.c
index c12043b88..d8c26aca2 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -376,12 +376,12 @@ parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp,
*argv = "--";
while (*++*argv) {
if (**argv == '-') {
- if(!argv[0][1]) {
+ if (!argv[0][1]) {
/* The pseudo-option `--' signifies the end of options. */
argv++;
goto doneoptions;
}
- if(*argv != args+1 || **argv != '-')
+ if (nam || *argv != args+1 || **argv != '-')
goto badoptionstring;
/* GNU-style long options */
++*argv;
@@ -790,7 +790,7 @@ init_term(void)
tcstr[TCCLEARSCREEN] = ztrdup("\14");
tclen[TCCLEARSCREEN] = 1;
}
- rprompt_indent = 1;
+ rprompt_indent = 1; /* If you change this, update rprompt_indent_unsetfn() */
/* The following is an attempt at a heuristic,
* but it fails in some cases */
/* rprompt_indent = ((hasam && !hasbw) || hasye || !tccan(TCLEFT)); */
diff --git a/Src/input.c b/Src/input.c
index eb968ea72..9787dedf6 100644
--- a/Src/input.c
+++ b/Src/input.c
@@ -144,9 +144,10 @@ shingetline(void)
int q = queue_signal_level();
p = buf;
+ winch_unblock();
+ dont_queue_signals();
for (;;) {
- winch_unblock();
- dont_queue_signals();
+ /* Can't fgets() here because we need to accept '\0' bytes */
do {
errno = 0;
c = fgetc(bshin);
@@ -176,7 +177,8 @@ shingetline(void)
ll += p - buf;
line[ll] = '\0';
p = buf;
- unqueue_signals();
+ winch_unblock();
+ dont_queue_signals();
}
}
}
@@ -670,3 +672,30 @@ ingetptr(void)
{
return inbufptr;
}
+
+/*
+ * Check if the current input line, including continuations, is
+ * expanding an alias. This does not detect alias expansions that
+ * have been fully processed and popped from the input stack.
+ * If there is an alias, the most recently expanded is returned,
+ * else NULL.
+ */
+
+/**/
+char *input_hasalias(void)
+{
+ int flags = inbufflags;
+ struct instacks *instackptr = instacktop;
+
+ for (;;)
+ {
+ if (!(flags & INP_CONT))
+ break;
+ instackptr--;
+ if (instackptr->alias)
+ return instackptr->alias->node.nam;
+ flags = instackptr->flags;
+ }
+
+ return NULL;
+}
diff --git a/Src/jobs.c b/Src/jobs.c
index d1b98ac4d..66dfb5a7e 100644
--- a/Src/jobs.c
+++ b/Src/jobs.c
@@ -277,6 +277,10 @@ handle_sub(int job, int fg)
(!jn->procs->next || cp || jn->procs->pid != jn->gleader))
attachtty(jn->gleader);
kill(sj->other, SIGCONT);
+ if (jn->stat & STAT_DISOWN)
+ {
+ deletejob(jn, 1);
+ }
}
curjob = jn - jobtab;
} else if (sj->stat & STAT_STOPPED) {
@@ -2288,8 +2292,10 @@ bin_fg(char *name, char **argv, Options ops, int func)
case BIN_FG:
case BIN_BG:
case BIN_WAIT:
- if (func == BIN_BG)
+ if (func == BIN_BG) {
jobtab[job].stat |= STAT_NOSTTY;
+ jobtab[job].stat &= ~STAT_CURSH;
+ }
if ((stopped = (jobtab[job].stat & STAT_STOPPED))) {
makerunning(jobtab + job);
if (func == BIN_BG) {
@@ -2373,6 +2379,10 @@ bin_fg(char *name, char **argv, Options ops, int func)
printjob(job + (oldjobtab ? oldjobtab : jobtab), lng, 2);
break;
case BIN_DISOWN:
+ if (jobtab[job].stat & STAT_SUPERJOB) {
+ jobtab[job].stat |= STAT_DISOWN;
+ continue;
+ }
if (jobtab[job].stat & STAT_STOPPED) {
char buf[20], *pids = "";
diff --git a/Src/lex.c b/Src/lex.c
index 889612825..b2d9b3f42 100644
--- a/Src/lex.c
+++ b/Src/lex.c
@@ -1359,17 +1359,13 @@ gettokstr(int c, int sub)
case LX2_DASH:
/*
* - shouldn't be treated as a special character unless
- * we're in a pattern. Howeve,simply counting "[" doesn't
- * work as []a-z] is a valid expression and we don't know
- * down here what this "[" is for as $foo[stuff] is valid
- * in zsh. So just detect an opening [, which is enough
- * to turn this into a pattern; the Dash will be harmlessly
- * untokenised if not wanted.
+ * we're in a pattern. Unfortunately, working out for
+ * sure in complicated expressions whether we're in a
+ * pattern is tricky. So we'll make it special and
+ * turn it back any time we don't need it special.
+ * This is not ideal as it's a lot of work.
*/
- if (seen_brct)
- c = Dash;
- else
- c = '-';
+ c = Dash;
break;
case LX2_BANG:
/*
@@ -2064,9 +2060,7 @@ skipcomm(void)
int new_lexstop, new_lex_add_raw;
int save_infor = infor;
struct lexbufstate new_lexbuf;
- int noalias = noaliases;
- noaliases = 1;
infor = 0;
cmdpush(CS_CMDSUBST);
SETPARBEGIN
@@ -2193,7 +2187,6 @@ skipcomm(void)
SETPAREND
cmdpop();
infor = save_infor;
- noaliases = noalias;
return lexstop;
#endif
diff --git a/Src/linklist.c b/Src/linklist.c
index 3aa8125d9..85d9bb367 100644
--- a/Src/linklist.c
+++ b/Src/linklist.c
@@ -348,6 +348,35 @@ newsizedlist(int size)
}
/*
+ * Join two linked lists. Neither may be null, though either
+ * may be empty.
+ *
+ * It is assumed the pieces come from the heap, but if not it is
+ * safe to free LinkList second.
+ */
+
+/**/
+mod_export LinkList
+joinlists(LinkList first, LinkList second)
+{
+ LinkNode moveme = firstnode(second);
+ if (moveme) {
+ if (firstnode(first)) {
+ LinkNode anchor = lastnode(first);
+ anchor->next = moveme;
+ moveme->prev = anchor;
+ } else {
+ first->list.first = moveme;
+ moveme->prev = &first->node;
+ }
+ first->list.last = second->list.last;
+
+ second->list.first = second->list.last = NULL;
+ }
+ return first;
+}
+
+/*
* Return the node whose data is the pointer "dat", else NULL.
* Can be used as a boolean test.
*/
diff --git a/Src/loop.c b/Src/loop.c
index ae87b2f5f..f7eae307b 100644
--- a/Src/loop.c
+++ b/Src/loop.c
@@ -620,7 +620,9 @@ execcase(Estate state, int do_exec)
spprog = state->prog->pats + npat;
pprog = NULL;
pat = NULL;
-
+
+ queue_signals();
+
if (isset(XTRACE)) {
int htok = 0;
pat = dupstring(ecrawstr(state->prog, state->pc, &htok));
@@ -657,6 +659,8 @@ execcase(Estate state, int do_exec)
patok = anypatok = 1;
state->pc += 2;
nalts--;
+
+ unqueue_signals();
}
state->pc += 2 * nalts;
if (isset(XTRACE)) {
diff --git a/Src/math.c b/Src/math.c
index 37981cf22..f9613001a 100644
--- a/Src/math.c
+++ b/Src/math.c
@@ -463,7 +463,7 @@ lexconstant(void)
char *nptr;
nptr = ptr;
- if (*nptr == '-')
+ if (IS_DASH(*nptr))
nptr++;
if (*nptr == '0') {
@@ -527,7 +527,7 @@ lexconstant(void)
}
if (*nptr == 'e' || *nptr == 'E') {
nptr++;
- if (*nptr == '+' || *nptr == '-')
+ if (*nptr == '+' || IS_DASH(*nptr))
nptr++;
while (idigit(*nptr) || *nptr == '_')
nptr++;
@@ -599,7 +599,8 @@ zzlex(void)
}
return (unary) ? UPLUS : PLUS;
case '-':
- if (*ptr == '-') {
+ case Dash:
+ if (IS_DASH(*ptr)) {
ptr++;
return (unary) ? PREMINUS : POSTMINUS;
}
@@ -974,7 +975,7 @@ callmathfunc(char *o)
a[strlen(a) - 1] = '\0';
if ((f = getmathfunc(n, 1))) {
- if (f->flags & MFF_STR) {
+ if ((f->flags & (MFF_STR|MFF_USERFUNC)) == MFF_STR) {
return f->sfunc(n, a, f->funcid);
} else {
int argc = 0;
@@ -987,22 +988,34 @@ callmathfunc(char *o)
addlinknode(l, n);
}
- while (iblank(*a))
- a++;
+ if (f->flags & MFF_STR) {
+ if (!*a) {
+ addlinknode(l, dupstring(""));
+ argc++;
+ }
+ } else {
+ while (iblank(*a))
+ a++;
+ }
while (*a) {
if (*a) {
argc++;
if (f->flags & MFF_USERFUNC) {
/* need to pass strings */
char *str;
- marg = mathevall(a, MPREC_ARG, &a);
- if (marg.type & MN_FLOAT) {
- /* convfloat is off the heap */
- str = convfloat(marg.u.d, 0, 0, NULL);
+ if (f->flags & MFF_STR) {
+ str = dupstring(a);
+ a = "";
} else {
- char buf[BDIGBUFSIZE];
- convbase(buf, marg.u.l, 10);
- str = dupstring(buf);
+ marg = mathevall(a, MPREC_ARG, &a);
+ if (marg.type & MN_FLOAT) {
+ /* convfloat is off the heap */
+ str = convfloat(marg.u.d, 0, 0, NULL);
+ } else {
+ char buf[BDIGBUFSIZE];
+ convbase(buf, marg.u.l, 10);
+ str = dupstring(buf);
+ }
}
addlinknode(l, str);
} else {
diff --git a/Src/module.c b/Src/module.c
index 41f142adb..21d68b1ac 100644
--- a/Src/module.c
+++ b/Src/module.c
@@ -2326,7 +2326,7 @@ load_module(char const *name, Feature_enables enablesarr, int silent)
/**/
mod_export int
-require_module(const char *module, Feature_enables features)
+require_module(const char *module, Feature_enables features, int silent)
{
Module m = NULL;
int ret = 0;
@@ -2336,7 +2336,7 @@ require_module(const char *module, Feature_enables features)
m = find_module(module, FINDMOD_ALIASP, &module);
if (!m || !m->u.handle ||
(m->node.flags & MOD_UNLOAD))
- ret = load_module(module, features, 0);
+ ret = load_module(module, features, silent);
else
ret = do_module_features(m, features, 0);
unqueue_signals();
@@ -2972,7 +2972,7 @@ bin_zmodload_load(char *nam, char **args, Options ops)
} else {
/* load modules */
for (; *args; args++) {
- int tmpret = require_module(*args, NULL);
+ int tmpret = require_module(*args, NULL, OPT_ISSET(ops,'s'));
if (tmpret && ret != 1)
ret = tmpret;
}
@@ -3242,7 +3242,7 @@ bin_zmodload_features(const char *nam, char **args, Options ops)
fep->str = NULL;
fep->pat = NULL;
- return require_module(modname, features);
+ return require_module(modname, features, OPT_ISSET(ops,'s'));
}
@@ -3403,14 +3403,14 @@ ensurefeature(const char *modname, const char *prefix, const char *feature)
struct feature_enables features[2];
if (!feature)
- return require_module(modname, NULL);
+ return require_module(modname, NULL, 0);
f = dyncat(prefix, feature);
features[0].str = f;
features[0].pat = NULL;
features[1].str = NULL;
features[1].pat = NULL;
- return require_module(modname, features);
+ return require_module(modname, features, 0);
}
/*
diff --git a/Src/options.c b/Src/options.c
index 18619c851..2b5795bab 100644
--- a/Src/options.c
+++ b/Src/options.c
@@ -78,6 +78,7 @@ mod_export HashTable optiontab;
*/
static struct optname optns[] = {
{{NULL, "aliases", OPT_EMULATE|OPT_ALL}, ALIASESOPT},
+{{NULL, "aliasfuncdef", OPT_EMULATE|OPT_BOURNE}, ALIASFUNCDEF},
{{NULL, "allexport", OPT_EMULATE}, ALLEXPORT},
{{NULL, "alwayslastprompt", OPT_ALL}, ALWAYSLASTPROMPT},
{{NULL, "alwaystoend", 0}, ALWAYSTOEND},
@@ -257,6 +258,7 @@ static struct optname optns[] = {
{{NULL, "verbose", 0}, VERBOSE},
{{NULL, "vi", 0}, VIMODE},
{{NULL, "warncreateglobal", OPT_EMULATE}, WARNCREATEGLOBAL},
+{{NULL, "warnnestedvar", OPT_EMULATE}, WARNNESTEDVAR},
{{NULL, "xtrace", 0}, XTRACE},
{{NULL, "zle", OPT_SPECIAL}, USEZLE},
{{NULL, "braceexpand", OPT_ALIAS}, /* ksh/bash */ -IGNOREBRACES},
@@ -645,7 +647,7 @@ bin_setopt(char *nam, char **args, UNUSED(Options ops), int isun)
/* Expand the current arg. */
tokenize(s);
- if (!(pprog = patcompile(s, PAT_STATIC, NULL))) {
+ if (!(pprog = patcompile(s, PAT_HEAPDUP, NULL))) {
zwarnnam(nam, "bad pattern: %s", *args);
continue;
}
diff --git a/Src/params.c b/Src/params.c
index c64d7486b..6fbee880c 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -128,6 +128,11 @@ struct timeval shtimer;
/**/
mod_export int termflags;
+/* Forward declaration */
+
+static void
+rprompt_indent_unsetfn(Param pm, int exp);
+
/* Standard methods for get/set/unset pointers in parameters */
/**/
@@ -241,6 +246,9 @@ static const struct gsu_integer argc_gsu =
static const struct gsu_array pipestatus_gsu =
{ pipestatgetfn, pipestatsetfn, stdunsetfn };
+static const struct gsu_integer rprompt_indent_gsu =
+{ intvargetfn, zlevarsetfn, rprompt_indent_unsetfn };
+
/* Nodes for special parameters for parameter hash table */
#ifdef HAVE_UNION_INIT
@@ -327,7 +335,7 @@ IPDEF4("ZSH_SUBSHELL", &zsh_subshell),
#define IPDEF5U(A,B,F) {{NULL,A,PM_INTEGER|PM_SPECIAL|PM_UNSET},BR((void *)B),GSU(F),10,0,NULL,NULL,NULL,0}
IPDEF5("COLUMNS", &zterm_columns, zlevar_gsu),
IPDEF5("LINES", &zterm_lines, zlevar_gsu),
-IPDEF5U("ZLE_RPROMPT_INDENT", &rprompt_indent, zlevar_gsu),
+IPDEF5U("ZLE_RPROMPT_INDENT", &rprompt_indent, rprompt_indent_gsu),
IPDEF5("SHLVL", &shlvl, varinteger_gsu),
/* Don't import internal integer status variables. */
@@ -353,6 +361,17 @@ IPDEF7("PS3", &prompt3),
IPDEF7R("PS4", &prompt4),
IPDEF7("SPROMPT", &sprompt),
+#define IPDEF9F(A,B,C,D) {{NULL,A,D|PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT},BR((void *)B),GSU(vararray_gsu),0,0,NULL,C,NULL,0}
+#define IPDEF9(A,B,C) IPDEF9F(A,B,C,0)
+IPDEF9F("*", &pparams, NULL, PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT|PM_READONLY),
+IPDEF9F("@", &pparams, NULL, PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT|PM_READONLY),
+
+/*
+ * This empty row indicates the end of parameters available in
+ * all emulations.
+ */
+{{NULL,NULL,0},BR(NULL),NULL_GSU,0,0,NULL,NULL,NULL,0},
+
#define IPDEF8(A,B,C,D) {{NULL,A,D|PM_SCALAR|PM_SPECIAL},BR((void *)B),GSU(colonarr_gsu),0,0,NULL,C,NULL,0}
IPDEF8("CDPATH", &cdpath, "cdpath", 0),
IPDEF8("FIGNORE", &fignore, "fignore", 0),
@@ -366,17 +385,6 @@ IPDEF8("ZSH_EVAL_CONTEXT", &zsh_eval_context, "zsh_eval_context", PM_READONLY),
/* MODULE_PATH is not imported for security reasons */
IPDEF8("MODULE_PATH", &module_path, "module_path", PM_DONTIMPORT|PM_RESTRICTED),
-#define IPDEF9F(A,B,C,D) {{NULL,A,D|PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT},BR((void *)B),GSU(vararray_gsu),0,0,NULL,C,NULL,0}
-#define IPDEF9(A,B,C) IPDEF9F(A,B,C,0)
-IPDEF9F("*", &pparams, NULL, PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT|PM_READONLY),
-IPDEF9F("@", &pparams, NULL, PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT|PM_READONLY),
-
-/*
- * This empty row indicates the end of parameters available in
- * all emulations.
- */
-{{NULL,NULL,0},BR(NULL),NULL_GSU,0,0,NULL,NULL,NULL,0},
-
#define IPDEF10(A,B) {{NULL,A,PM_ARRAY|PM_SPECIAL},BR(NULL),GSU(B),10,0,NULL,NULL,NULL,0}
/*
@@ -416,6 +424,26 @@ IPDEF10("pipestatus", pipestatus_gsu),
};
/*
+ * Alternative versions of colon-separated path parameters for
+ * sh emulation. These don't link to the array versions.
+ */
+static initparam special_params_sh[] = {
+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),
+
+/* MODULE_PATH is not imported for security reasons */
+IPDEF8("MODULE_PATH", &module_path, NULL, PM_DONTIMPORT|PM_RESTRICTED),
+
+{{NULL,NULL,0},BR(NULL),NULL_GSU,0,0,NULL,NULL,NULL,0},
+};
+
+/*
* Special way of referring to the positional parameters. Unlike $*
* and $@, this is not readonly. This parameter is not directly
* visible in user space.
@@ -745,9 +773,13 @@ createparamtable(void)
/* Add the special parameters to the hash table */
for (ip = special_params; ip->node.nam; ip++)
paramtab->addnode(paramtab, ztrdup(ip->node.nam), ip);
- if (!EMULATION(EMULATE_SH|EMULATE_KSH))
+ if (EMULATION(EMULATE_SH|EMULATE_KSH)) {
+ for (ip = special_params_sh; ip->node.nam; ip++)
+ paramtab->addnode(paramtab, ztrdup(ip->node.nam), ip);
+ } else {
while ((++ip)->node.nam)
paramtab->addnode(paramtab, ztrdup(ip->node.nam), ip);
+ }
argvparam = (Param) &argvparam_pm;
@@ -1175,7 +1207,7 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w,
int *prevcharlen, int *nextcharlen)
{
int hasbeg = 0, word = 0, rev = 0, ind = 0, down = 0, l, i, ishash;
- int keymatch = 0, needtok = 0, arglen, len;
+ int keymatch = 0, needtok = 0, arglen, len, inpar = 0;
char *s = *str, *sep = NULL, *t, sav, *d, **ta, **p, *tt, c;
zlong num = 1, beg = 0, r = 0, quote_arg = 0;
Patprog pprog = NULL;
@@ -1314,8 +1346,9 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w,
}
for (t = s, i = 0;
- (c = *t) && ((c != Outbrack &&
- (ishash || c != ',')) || i); t++) {
+ (c = *t) &&
+ ((c != Outbrack && (ishash || c != ',')) || i || inpar);
+ t++) {
/* Untokenize inull() except before brackets and double-quotes */
if (inull(c)) {
c = t[1];
@@ -1336,6 +1369,10 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w,
i++;
else if (c == ']' || c == Outbrack)
i--;
+ if (c == '(' || c == Inpar)
+ inpar++;
+ else if (c == ')' || c == Outpar)
+ inpar--;
if (ispecial(c))
needtok = 1;
}
@@ -1979,7 +2016,9 @@ fetchvalue(Value v, char **pptr, int bracks, int flags)
*s++ = '$';
else if (c == Star)
*s++ = '*';
- else if (c == '#' || c == '-' || c == '?' || c == '$' ||
+ else if (IS_DASH(c))
+ *s++ = '-';
+ else if (c == '#' || c == '?' || c == '$' ||
c == '!' || c == '@' || c == '*')
s++;
else
@@ -2708,24 +2747,73 @@ setarrvalue(Value v, char **val)
post_assignment_length += pre_assignment_length - v->end;
}
- p = new = (char **) zalloc(sizeof(char *)
- * (post_assignment_length + 1));
+ if (pre_assignment_length == post_assignment_length
+ && v->pm->gsu.a->setfn == arrsetfn
+ /* ... and isn't something that arrsetfn() treats specially */
+ && 0 == (v->pm->node.flags & (PM_SPECIAL|PM_UNIQUE))
+ && NULL == v->pm->ename)
+ {
+ /* v->start is 0-based */
+ p = old + v->start;
+ for (r = val; *r;) {
+ /* Free previous string */
+ zsfree(*p);
+ /* Give away ownership of the string */
+ *p++ = *r++;
+ }
+ } else {
+ /* arr+=( ... )
+ * arr[${#arr}+x,...]=( ... ) */
+ if (post_assignment_length > pre_assignment_length &&
+ pre_assignment_length <= v->start &&
+ pre_assignment_length > 0 &&
+ v->pm->gsu.a->setfn == arrsetfn)
+ {
+ p = new = (char **) zrealloc(old, sizeof(char *)
+ * (post_assignment_length + 1));
+
+ p += pre_assignment_length; /* after old elements */
+
+ /* Consider 1 < 0, case for a=( 1 ); a[1,..] =
+ * 1 < 1, case for a=( 1 ); a[2,..] = */
+ if (pre_assignment_length < v->start) {
+ for (i = pre_assignment_length; i < v->start; i++) {
+ *p++ = ztrdup("");
+ }
+ }
+
+ for (r = val; *r;) {
+ /* Give away ownership of the string */
+ *p++ = *r++;
+ }
+
+ /* v->end doesn't matter:
+ * a=( 1 2 ); a[4,100]=( a b ); echo "${(q@)a}"
+ * 1 2 '' a b */
+ *p = NULL;
+
+ v->pm->u.arr = NULL;
+ v->pm->gsu.a->setfn(v->pm, new);
+ } else {
+ p = new = (char **) zalloc(sizeof(char *)
+ * (post_assignment_length + 1));
+ for (i = 0; i < v->start; i++)
+ *p++ = i < pre_assignment_length ? ztrdup(*q++) : ztrdup("");
+ for (r = val; *r;) {
+ /* Give away ownership of the string */
+ *p++ = *r++;
+ }
+ if (v->end < pre_assignment_length)
+ for (q = old + v->end; *q;)
+ *p++ = ztrdup(*q++);
+ *p = NULL;
+
+ v->pm->gsu.a->setfn(v->pm, new);
+ }
- for (i = 0; i < v->start; i++)
- *p++ = i < pre_assignment_length ? ztrdup(*q++) : ztrdup("");
- for (r = val; *r;) {
- /* Give away ownership of the string */
- *p++ = *r++;
+ DPUTS2(p - new != post_assignment_length, "setarrvalue: wrong allocation: %d 1= %lu",
+ post_assignment_length, (unsigned long)(p - new));
}
- if (v->end < pre_assignment_length)
- for (q = old + v->end; *q;)
- *p++ = ztrdup(*q++);
- *p = NULL;
-
- DPUTS2(p - new != post_assignment_length, "setarrvalue: wrong allocation: %d 1= %lu",
- post_assignment_length, (unsigned long)(p - new));
-
- v->pm->gsu.a->setfn(v->pm, new);
/* Ownership of all strings has been
* given away, can plainly free */
@@ -2833,20 +2921,51 @@ gethkparam(char *s)
return NULL;
}
+/*
+ * Function behind WARNCREATEGLOBAL and WARNNESTEDVAR option.
+ *
+ * For WARNNESTEDVAR:
+ * Called when the variable is created.
+ * Apply heuristics to see if this variable was just created
+ * globally but in a local context.
+ *
+ * For WARNNESTEDVAR:
+ * Called when the variable already exists and is set.
+ * Apply heuristics to see if this variable is setting
+ * a variable that was created in a less nested function
+ * or globally.
+ */
+
/**/
static void
-check_warn_create(Param pm, const char *pmtype)
+check_warn_pm(Param pm, const char *pmtype, int created,
+ int may_warn_about_nested_vars)
{
Funcstack i;
- if (pm->level != 0 || (pm->node.flags & PM_SPECIAL))
+ if (!may_warn_about_nested_vars && !created)
+ return;
+
+ if (created && isset(WARNCREATEGLOBAL)) {
+ if (locallevel <= forklevel || pm->level != 0)
+ return;
+ } else if (!created && isset(WARNNESTEDVAR)) {
+ if (pm->level >= locallevel)
+ return;
+ } else
+ return;
+
+ if (pm->node.flags & PM_SPECIAL)
return;
for (i = funcstack; i; i = i->prev) {
if (i->tp == FS_FUNC) {
+ char *msg;
DPUTS(!i->name, "funcstack entry with no name");
- zwarn("%s parameter %s created globally in function %s",
- pmtype, pm->node.nam, i->name);
+ msg = created ?
+ "%s parameter %s created globally in function %s" :
+ "%s parameter %s set in enclosing scope in function %s";
+ zwarn(msg, pmtype, pm->node.nam, i->name);
break;
}
}
@@ -2862,7 +2981,7 @@ assignsparam(char *s, char *val, int flags)
char *ss, *copy, *var;
size_t lvar;
mnumber lhs, rhs;
- int sstart;
+ int sstart, created = 0;
if (!isident(s)) {
zerr("not an identifier: %s", s);
@@ -2873,9 +2992,10 @@ assignsparam(char *s, char *val, int flags)
queue_signals();
if ((ss = strchr(s, '['))) {
*ss = '\0';
- if (!(v = getvalue(&vbuf, &s, 1)))
+ if (!(v = getvalue(&vbuf, &s, 1))) {
createparam(t, PM_ARRAY);
- else {
+ created = 1;
+ } else {
if (v->pm->node.flags & PM_READONLY) {
zerr("read-only variable: %s", v->pm->node.nam);
*ss = '[';
@@ -2883,23 +3003,27 @@ assignsparam(char *s, char *val, int flags)
unqueue_signals();
return NULL;
}
- flags &= ~ASSPM_WARN_CREATE;
+ /*
+ * Parameter defined here is a temporary bogus one.
+ * Don't warn about anything.
+ */
+ flags &= ~ASSPM_WARN;
}
*ss = '[';
v = NULL;
} else {
- if (!(v = getvalue(&vbuf, &s, 1)))
+ if (!(v = getvalue(&vbuf, &s, 1))) {
createparam(t, PM_SCALAR);
- else if ((((v->pm->node.flags & PM_ARRAY) && !(flags & ASSPM_AUGMENT)) ||
+ created = 1;
+ } else if ((((v->pm->node.flags & PM_ARRAY) && !(flags & ASSPM_AUGMENT)) ||
(v->pm->node.flags & PM_HASHED)) &&
!(v->pm->node.flags & (PM_SPECIAL|PM_TIED)) &&
unset(KSHARRAYS)) {
unsetparam(t);
createparam(t, PM_SCALAR);
+ /* not regarded as a new creation */
v = NULL;
}
- else
- flags &= ~ASSPM_WARN_CREATE;
}
if (!v && !(v = getvalue(&vbuf, &t, 1))) {
unqueue_signals();
@@ -2907,8 +3031,8 @@ assignsparam(char *s, char *val, int flags)
/* errflag |= ERRFLAG_ERROR; */
return NULL;
}
- if (flags & ASSPM_WARN_CREATE)
- check_warn_create(v->pm, "scalar");
+ if (flags & ASSPM_WARN)
+ check_warn_pm(v->pm, "scalar", created, 1);
if (flags & ASSPM_AUGMENT) {
if (v->start == 0 && v->end == -1) {
switch (PM_TYPE(v->pm->node.flags)) {
@@ -2989,9 +3113,7 @@ assignsparam(char *s, char *val, int flags)
mod_export Param
setsparam(char *s, char *val)
{
- return assignsparam(
- s, val, isset(WARNCREATEGLOBAL) && locallevel > forklevel ?
- ASSPM_WARN_CREATE : 0);
+ return assignsparam(s, val, ASSPM_WARN);
}
/**/
@@ -3002,6 +3124,8 @@ assignaparam(char *s, char **val, int flags)
Value v;
char *t = s;
char *ss;
+ int created = 0;
+ int may_warn_about_nested_vars = 1;
if (!isident(s)) {
zerr("not an identifier: %s", s);
@@ -3012,10 +3136,12 @@ assignaparam(char *s, char **val, int flags)
queue_signals();
if ((ss = strchr(s, '['))) {
*ss = '\0';
- if (!(v = getvalue(&vbuf, &s, 1)))
+ if (!(v = getvalue(&vbuf, &s, 1))) {
createparam(t, PM_ARRAY);
- else
- flags &= ~ASSPM_WARN_CREATE;
+ created = 1;
+ } else {
+ may_warn_about_nested_vars = 0;
+ }
*ss = '[';
if (v && PM_TYPE(v->pm->node.flags) == PM_HASHED) {
unqueue_signals();
@@ -3027,9 +3153,10 @@ assignaparam(char *s, char **val, int flags)
}
v = NULL;
} else {
- if (!(v = fetchvalue(&vbuf, &s, 1, SCANPM_ASSIGNING)))
+ if (!(v = fetchvalue(&vbuf, &s, 1, SCANPM_ASSIGNING))) {
createparam(t, PM_ARRAY);
- else if (!(PM_TYPE(v->pm->node.flags) & (PM_ARRAY|PM_HASHED)) &&
+ created = 1;
+ } else if (!(PM_TYPE(v->pm->node.flags) & (PM_ARRAY|PM_HASHED)) &&
!(v->pm->node.flags & (PM_SPECIAL|PM_TIED))) {
int uniq = v->pm->node.flags & PM_UNIQUE;
if (flags & ASSPM_AUGMENT) {
@@ -3047,8 +3174,6 @@ assignaparam(char *s, char **val, int flags)
createparam(t, PM_ARRAY | uniq);
v = NULL;
}
- else
- flags &= ~ASSPM_WARN_CREATE;
}
if (!v)
if (!(v = fetchvalue(&vbuf, &t, 1, SCANPM_ASSIGNING))) {
@@ -3058,8 +3183,8 @@ assignaparam(char *s, char **val, int flags)
return NULL;
}
- if (flags & ASSPM_WARN_CREATE)
- check_warn_create(v->pm, "array");
+ if (flags & ASSPM_WARN)
+ check_warn_pm(v->pm, "array", created, may_warn_about_nested_vars);
if (flags & ASSPM_AUGMENT) {
if (v->start == 0 && v->end == -1) {
if (PM_TYPE(v->pm->node.flags) & PM_ARRAY) {
@@ -3087,9 +3212,7 @@ assignaparam(char *s, char **val, int flags)
mod_export Param
setaparam(char *s, char **aval)
{
- return assignaparam(
- s, aval, isset(WARNCREATEGLOBAL) && locallevel > forklevel ?
- ASSPM_WARN_CREATE : 0);
+ return assignaparam(s, aval, ASSPM_WARN);
}
/**/
@@ -3118,13 +3241,18 @@ sethparam(char *s, char **val)
queue_signals();
if (!(v = fetchvalue(&vbuf, &s, 1, SCANPM_ASSIGNING))) {
createparam(t, PM_HASHED);
- checkcreate = isset(WARNCREATEGLOBAL) && locallevel > forklevel;
- } else if (!(PM_TYPE(v->pm->node.flags) & PM_HASHED) &&
- !(v->pm->node.flags & PM_SPECIAL)) {
- unsetparam(t);
- /* no WARNCREATEGLOBAL check here as parameter already existed */
- createparam(t, PM_HASHED);
- v = NULL;
+ checkcreate = 1;
+ } else if (!(PM_TYPE(v->pm->node.flags) & PM_HASHED)) {
+ if (!(v->pm->node.flags & PM_SPECIAL)) {
+ unsetparam(t);
+ /* no WARNCREATEGLOBAL check here as parameter already existed */
+ createparam(t, PM_HASHED);
+ v = NULL;
+ } else {
+ zerr("%s: can't change type of a special parameter", t);
+ unqueue_signals();
+ return NULL;
+ }
}
if (!v)
if (!(v = fetchvalue(&vbuf, &t, 1, SCANPM_ASSIGNING))) {
@@ -3132,8 +3260,7 @@ sethparam(char *s, char **val)
/* errflag |= ERRFLAG_ERROR; */
return NULL;
}
- if (checkcreate)
- check_warn_create(v->pm, "associative array");
+ check_warn_pm(v->pm, "associative array", checkcreate, 1);
setarrvalue(v, val);
unqueue_signals();
return v->pm;
@@ -3142,11 +3269,12 @@ sethparam(char *s, char **val)
/*
* Set a generic shell number, floating point or integer.
+ * Option to warn on setting.
*/
/**/
-Param
-setnparam(char *s, mnumber val)
+mod_export Param
+assignnparam(char *s, mnumber val, int flags)
{
struct value vbuf;
Value v;
@@ -3198,14 +3326,41 @@ setnparam(char *s, mnumber val)
unqueue_signals();
return NULL;
}
- if (!was_unset && isset(WARNCREATEGLOBAL) && locallevel > forklevel)
- check_warn_create(v->pm, "numeric");
+ if (flags & ASSPM_WARN)
+ check_warn_pm(v->pm, "numeric", !was_unset, 1);
+ } else {
+ if (flags & ASSPM_WARN)
+ check_warn_pm(v->pm, "numeric", 0, 1);
}
setnumvalue(v, val);
unqueue_signals();
return v->pm;
}
+/*
+ * Set a generic shell number, floating point or integer.
+ * Warn on setting based on option.
+ */
+
+/**/
+mod_export Param
+setnparam(char *s, mnumber val)
+{
+ return assignnparam(s, val, ASSPM_WARN);
+}
+
+/* Simplified interface to assignnparam */
+
+/**/
+mod_export Param
+assigniparam(char *s, zlong val, int flags)
+{
+ mnumber mnval;
+ mnval.type = MN_INTEGER;
+ mnval.u.l = val;
+ return assignnparam(s, mnval, flags);
+}
+
/* Simplified interface to setnparam */
/**/
@@ -3215,7 +3370,7 @@ setiparam(char *s, zlong val)
mnumber mnval;
mnval.type = MN_INTEGER;
mnval.u.l = val;
- return setnparam(s, mnval);
+ return assignnparam(s, mnval, ASSPM_WARN);
}
/*
@@ -3234,10 +3389,7 @@ setiparam_no_convert(char *s, zlong val)
*/
char buf[BDIGBUFSIZE];
convbase(buf, val, 10);
- return assignsparam(
- s, ztrdup(buf),
- isset(WARNCREATEGLOBAL) && locallevel > forklevel ?
- ASSPM_WARN_CREATE : 0);
+ return assignsparam(s, ztrdup(buf), ASSPM_WARN);
}
/* Unset a parameter */
@@ -3458,6 +3610,8 @@ strsetfn(Param pm, char *x)
pm->node.flags |= PM_NAMEDDIR;
adduserdir(pm->node.nam, x, 0, 0);
}
+ /* If you update this function, you may need to update the
+ * `Implement remainder of strsetfn' block in assignstrvalue(). */
}
/* Function to get value of an array parameter */
@@ -3485,6 +3639,8 @@ arrsetfn(Param pm, char **x)
/* Arrays tied to colon-arrays may need to fix the environment */
if (pm->ename && x)
arrfixenv(pm->ename, x);
+ /* If you extend this function, update the list of conditions in
+ * setarrvalue(). */
}
/* Function to get value of an association parameter */
@@ -3621,6 +3777,16 @@ zlevarsetfn(Param pm, zlong x)
adjustwinsize(2 + (p == &zterm_columns));
}
+
+/* Implements gsu_integer.unsetfn for ZLE_RPROMPT_INDENT; see stdunsetfn() */
+
+static void
+rprompt_indent_unsetfn(Param pm, int exp)
+{
+ stdunsetfn(pm, exp);
+ rprompt_indent = 1; /* Keep this in sync with init_term() */
+}
+
/* Function to set value of generic special scalar *
* parameter. data is pointer to a character pointer *
* representing the scalar (string). */
@@ -3720,8 +3886,7 @@ colonarrsetfn(Param pm, char *x)
*dptr = colonsplit(x, pm->node.flags & PM_UNIQUE);
else
*dptr = mkarray(NULL);
- if (pm->ename)
- arrfixenv(pm->node.nam, *dptr);
+ arrfixenv(pm->node.nam, *dptr);
zsfree(x);
}
diff --git a/Src/parse.c b/Src/parse.c
index 50a0d5f9f..ba9cd61eb 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -394,9 +394,12 @@ ecdel(int p)
static wordcode
ecstrcode(char *s)
{
- int l, t = has_token(s);
+ int l, t;
+
+ unsigned val = hasher(s);
if ((l = strlen(s) + 1) && l <= 4) {
+ t = has_token(s);
wordcode c = (t ? 3 : 2);
switch (l) {
case 4: c |= ((wordcode) STOUC(s[2])) << 19;
@@ -410,16 +413,21 @@ ecstrcode(char *s)
int cmp;
for (pp = &ecstrs; (p = *pp); ) {
- if (!(cmp = p->nfunc - ecnfunc) && !(cmp = strcmp(p->str, s)))
+ if (!(cmp = p->nfunc - ecnfunc) && !(cmp = (((signed)p->hashval) - ((signed)val))) && !(cmp = strcmp(p->str, s))) {
return p->offs;
+ }
pp = (cmp < 0 ? &(p->left) : &(p->right));
}
+
+ t = has_token(s);
+
p = *pp = (Eccstr) zhalloc(sizeof(*p));
p->left = p->right = 0;
p->offs = ((ecsoffs - ecssub) << 2) | (t ? 1 : 0);
p->aoffs = ecsoffs;
p->str = s;
p->nfunc = ecnfunc;
+ p->hashval = val;
ecsoffs += l;
return p->offs;
@@ -1738,6 +1746,7 @@ par_simple(int *cmplx, int nr)
{
int oecused = ecused, isnull = 1, r, argc = 0, p, isfunc = 0, sr = 0;
int c = *cmplx, nrediradd, assignments = 0, ppost = 0, is_typeset = 0;
+ char *hasalias = input_hasalias();
wordcode postassigns = 0;
r = ecused;
@@ -1809,6 +1818,8 @@ par_simple(int *cmplx, int nr)
} else
break;
zshlex();
+ if (!hasalias)
+ hasalias = input_hasalias();
}
if (tok == AMPER || tok == AMPERBANG)
YYERROR(oecused);
@@ -1833,12 +1844,14 @@ par_simple(int *cmplx, int nr)
if (*ptr == Outbrace && ptr > tokstr + 1)
{
- if (itype_end(tokstr+1, IIDENT, 0) >= ptr - 1)
+ if (itype_end(tokstr+1, IIDENT, 0) >= ptr)
{
char *toksave = tokstr;
char *idstring = dupstrpfx(tokstr+1, eptr-tokstr-1);
redir_var = 1;
zshlex();
+ if (!hasalias)
+ hasalias = input_hasalias();
if (IS_REDIROP(tok) && tokfd == -1)
{
@@ -1874,6 +1887,8 @@ par_simple(int *cmplx, int nr)
argc++;
}
zshlex();
+ if (!hasalias)
+ hasalias = input_hasalias();
}
} else if (IS_REDIROP(tok)) {
*cmplx = c = 1;
@@ -1902,6 +1917,8 @@ par_simple(int *cmplx, int nr)
ecstr(name);
ecstr(str);
zshlex();
+ if (!hasalias)
+ hasalias = input_hasalias();
} else if (tok == ENVARRAY) {
int n, parr;
@@ -1936,6 +1953,11 @@ par_simple(int *cmplx, int nr)
/* Error if preceding assignments */
if (assignments || postassigns)
YYERROR(oecused);
+ if (hasalias && !isset(ALIASFUNCDEF) && argc &&
+ hasalias != input_hasalias()) {
+ zwarn("defining function based on alias `%s'", hasalias);
+ YYERROR(oecused);
+ }
*cmplx = c;
lineno = 0;
@@ -2016,10 +2038,21 @@ par_simple(int *cmplx, int nr)
/* Unnamed function */
int parg = ecadd(0);
ecadd(0);
- while (tok == STRING) {
- ecstr(tokstr);
- argc++;
- zshlex();
+ while (tok == STRING || IS_REDIROP(tok)) {
+ if (tok == STRING)
+ {
+ ecstr(tokstr);
+ argc++;
+ zshlex();
+ } else {
+ *cmplx = c = 1;
+ nrediradd = par_redir(&r, NULL);
+ p += nrediradd;
+ if (ppost)
+ ppost += nrediradd;
+ sr += nrediradd;
+ parg += nrediradd;
+ }
}
if (argc > 0)
*cmplx = 1;
@@ -2129,7 +2162,7 @@ par_redir(int *rp, char *idstring)
* the definition of WC_REDIR_WORDS. */
ecispace(r, ncodes);
*rp = r + ncodes;
- ecbuf[r] = WCB_REDIR(type);
+ ecbuf[r] = WCB_REDIR(type | REDIR_FROM_HEREDOC_MASK);
ecbuf[r + 1] = fd1;
/*
@@ -2303,6 +2336,19 @@ par_cond_1(void)
}
/*
+ * Return 1 if condition matches. This also works for non-elided options.
+ *
+ * input is test string, may begin - or Dash.
+ * cond is condition following the -.
+ */
+static int check_cond(const char *input, const char *cond)
+{
+ if (!IS_DASH(input[0]))
+ return 0;
+ return !strcmp(input + 1, cond);
+}
+
+/*
* cond_2 : BANG cond_2
| INPAR { SEPER } cond_2 { SEPER } OUTPAR
| STRING STRING STRING
@@ -2328,7 +2374,7 @@ par_cond_2(void)
s1 = tokstr;
condlex();
/* ksh behavior: [ -t ] means [ -t 1 ]; bash disagrees */
- if (unset(POSIXBUILTINS) && !strcmp(s1, "-t"))
+ if (unset(POSIXBUILTINS) && check_cond(s1, "t"))
return par_cond_double(s1, dupstring("1"));
return par_cond_double(dupstring("-n"), s1);
}
@@ -2338,7 +2384,7 @@ par_cond_2(void)
if (!strcmp(*testargs, "=") ||
!strcmp(*testargs, "==") ||
!strcmp(*testargs, "!=") ||
- (**testargs == '-' && get_cond_num(*testargs + 1) >= 0)) {
+ (IS_DASH(**testargs) && get_cond_num(*testargs + 1) >= 0)) {
s1 = tokstr;
condlex();
s2 = tokstr;
@@ -2360,8 +2406,8 @@ par_cond_2(void)
* In "test" compatibility mode, "! -a ..." and "! -o ..."
* are treated as "[string] [and] ..." and "[string] [or] ...".
*/
- if (!(n_testargs > 1 &&
- (!strcmp(*testargs, "-a") || !strcmp(*testargs, "-o"))))
+ if (!(n_testargs > 1 && (check_cond(*testargs, "a") ||
+ check_cond(*testargs, "o"))))
{
condlex();
ecadd(WCB_COND(COND_NOT, 0));
@@ -2383,7 +2429,7 @@ par_cond_2(void)
return r;
}
s1 = tokstr;
- dble = (s1 && *s1 == '-'
+ dble = (s1 && IS_DASH(*s1)
&& (!n_testargs
|| strspn(s1+1, "abcdefghknoprstuvwxzLONGS") == 1)
&& !s1[2]);
@@ -2397,7 +2443,7 @@ par_cond_2(void)
YYERROR(ecused);
}
condlex();
- if (n_testargs == 2 && tok != STRING && tokstr && s1[0] == '-') {
+ if (n_testargs == 2 && tok != STRING && tokstr && IS_DASH(s1[0])) {
/*
* Something like "test -z" followed by a token.
* We'll turn the token into a string (we've also
@@ -2432,9 +2478,9 @@ par_cond_2(void)
} else
YYERROR(ecused);
}
- s2 = tokstr;
+ s2 = tokstr;
if (!n_testargs)
- dble = (s2 && *s2 == '-' && !s2[2]);
+ dble = (s2 && IS_DASH(*s2) && !s2[2]);
incond++; /* parentheses do globbing */
do condlex(); while (COND_SEP());
incond--; /* parentheses do grouping */
@@ -2462,7 +2508,7 @@ par_cond_2(void)
static int
par_cond_double(char *a, char *b)
{
- if (a[0] != '-' || !a[1])
+ if (!IS_DASH(a[0]) || !a[1])
COND_ERROR("parse error: condition expected: %s", a);
else if (!a[2] && strspn(a+1, "abcdefgknoprstuvwxzhLONGS") == 1) {
ecadd(WCB_COND(a[1], 0));
@@ -2520,7 +2566,7 @@ par_cond_triple(char *a, char *b, char *c)
ecadd(WCB_COND(COND_REGEX, 0));
ecstr(a);
ecstr(c);
- } else if (b[0] == '-') {
+ } else if (IS_DASH(b[0])) {
if ((t0 = get_cond_num(b + 1)) > -1) {
ecadd(WCB_COND(t0 + COND_NT, 0));
ecstr(a);
@@ -2531,7 +2577,7 @@ par_cond_triple(char *a, char *b, char *c)
ecstr(a);
ecstr(c);
}
- } else if (a[0] == '-' && a[1]) {
+ } else if (IS_DASH(a[0]) && a[1]) {
ecadd(WCB_COND(COND_MOD, 2));
ecstr(a);
ecstr(b);
@@ -2546,7 +2592,7 @@ par_cond_triple(char *a, char *b, char *c)
static int
par_cond_multi(char *a, LinkList l)
{
- if (a[0] != '-' || !a[1])
+ if (!IS_DASH(a[0]) || !a[1])
COND_ERROR("condition expected: %s", a);
else {
LinkNode n;
@@ -3242,10 +3288,10 @@ build_dump(char *nam, char *dump, char **files, int ali, int map, int flags)
for (hlen = FD_PRELEN, tlen = 0; *files; files++) {
struct stat st;
- if (!strcmp(*files, "-k")) {
+ if (check_cond(*files, "k")) {
flags = (flags & ~(FDHF_KSHLOAD | FDHF_ZSHLOAD)) | FDHF_KSHLOAD;
continue;
- } else if (!strcmp(*files, "-z")) {
+ } else if (check_cond(*files, "z")) {
flags = (flags & ~(FDHF_KSHLOAD | FDHF_ZSHLOAD)) | FDHF_ZSHLOAD;
continue;
}
@@ -3324,7 +3370,7 @@ cur_add_func(char *nam, Shfunc shf, LinkList names, LinkList progs,
return 1;
}
noaliases = (shf->node.flags & PM_UNALIASED);
- if (!(prog = getfpfunc(shf->node.nam, NULL, NULL)) ||
+ if (!(prog = getfpfunc(shf->node.nam, NULL, NULL, NULL, 0)) ||
prog == &dummy_eprog) {
noaliases = ona;
zwarnnam(nam, "can't load function: %s", shf->node.nam);
@@ -3399,6 +3445,7 @@ build_cur_dump(char *nam, char *dump, char **names, int match, int map,
for (; *names; names++) {
tokenize(pat = dupstring(*names));
+ /* Signal-safe here, caller queues signals */
if (!(pprog = patcompile(pat, PAT_STATIC, NULL))) {
zwarnnam(nam, "bad pattern: %s", *names);
close(dfd);
@@ -3566,7 +3613,7 @@ load_dump_file(char *dump, struct stat *sbuf, int other, int len)
/**/
Eprog
-try_dump_file(char *path, char *name, char *file, int *ksh)
+try_dump_file(char *path, char *name, char *file, int *ksh, int test_only)
{
Eprog prog;
struct stat std, stc, stn;
@@ -3575,7 +3622,7 @@ try_dump_file(char *path, char *name, char *file, int *ksh)
if (strsfx(FD_EXT, path)) {
queue_signals();
- prog = check_dump_file(path, NULL, name, ksh);
+ prog = check_dump_file(path, NULL, name, ksh, test_only);
unqueue_signals();
return prog;
}
@@ -3594,14 +3641,14 @@ try_dump_file(char *path, char *name, char *file, int *ksh)
if (!rd &&
(rc || std.st_mtime > stc.st_mtime) &&
(rn || std.st_mtime > stn.st_mtime) &&
- (prog = check_dump_file(dig, &std, name, ksh))) {
+ (prog = check_dump_file(dig, &std, name, ksh, test_only))) {
unqueue_signals();
return prog;
}
/* No digest file. Now look for the per-function compiled file. */
if (!rc &&
(rn || stc.st_mtime > stn.st_mtime) &&
- (prog = check_dump_file(wc, &stc, name, ksh))) {
+ (prog = check_dump_file(wc, &stc, name, ksh, test_only))) {
unqueue_signals();
return prog;
}
@@ -3629,7 +3676,7 @@ try_source_file(char *file)
if (strsfx(FD_EXT, file)) {
queue_signals();
- prog = check_dump_file(file, NULL, tail, NULL);
+ prog = check_dump_file(file, NULL, tail, NULL, 0);
unqueue_signals();
return prog;
}
@@ -3640,7 +3687,7 @@ try_source_file(char *file)
queue_signals();
if (!rc && (rn || stc.st_mtime > stn.st_mtime) &&
- (prog = check_dump_file(wc, &stc, tail, NULL))) {
+ (prog = check_dump_file(wc, &stc, tail, NULL, 0))) {
unqueue_signals();
return prog;
}
@@ -3653,7 +3700,8 @@ try_source_file(char *file)
/**/
static Eprog
-check_dump_file(char *file, struct stat *sbuf, char *name, int *ksh)
+check_dump_file(char *file, struct stat *sbuf, char *name, int *ksh,
+ int test_only)
{
int isrec = 0;
Wordcode d;
@@ -3695,6 +3743,11 @@ check_dump_file(char *file, struct stat *sbuf, char *name, int *ksh)
if ((h = dump_find_func(d, name))) {
/* Found the name. If the file is already mapped, return the eprog,
* otherwise map it and just go up. */
+ if (test_only)
+ {
+ /* This is all we need. Just return dummy. */
+ return &dummy_eprog;
+ }
#ifdef USE_MMAP
@@ -3731,7 +3784,7 @@ check_dump_file(char *file, struct stat *sbuf, char *name, int *ksh)
#endif
- {
+ {
Eprog prog;
Patprog *pp;
int np, fd, po = h->npats * sizeof(Patprog);
diff --git a/Src/pattern.c b/Src/pattern.c
index 1f2e94bd9..fc7c73739 100644
--- a/Src/pattern.c
+++ b/Src/pattern.c
@@ -668,15 +668,9 @@ patcompile(char *exp, int inflags, char **endexp)
if (imeta(*mtest))
nmeta++;
if (nmeta) {
- char *oldpatout = patout;
- ptrdiff_t pd;
patadd(NULL, 0, nmeta, 0);
- /*
- * Yuk.
- */
p = (Patprog)patout;
- pd = patout - oldpatout;
- opnd += pd;
+ opnd = dupstring_wlen(opnd, oplen);
dst = patout + startoff;
}
@@ -1527,7 +1521,7 @@ patcomppiece(int *flagp, int paren)
patparse = nptr;
len |= 1;
}
- DPUTS(*patparse != '-', "BUG: - missing from numeric glob");
+ DPUTS(!IS_DASH(*patparse), "BUG: - missing from numeric glob");
patparse++;
if (idigit(*patparse)) {
to = (zrange_t) zstrtol((char *)patparse,
@@ -3631,7 +3625,7 @@ mb_patmatchrange(char *range, wchar_t ch, int zmb_ind, wint_t *indptr, int *mtp)
return 1;
break;
case PP_PRINT:
- if (iswprint(ch))
+ if (WC_ISPRINT(ch))
return 1;
break;
case PP_PUNCT:
diff --git a/Src/prompt.c b/Src/prompt.c
index ee77c8bc8..c478e69fb 100644
--- a/Src/prompt.c
+++ b/Src/prompt.c
@@ -399,7 +399,7 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep)
test = 1;
break;
case 'V':
- if (arrlen_ge(psvar, arg)) {
+ if (psvar && *psvar && arrlen_ge(psvar, arg)) {
if (*psvar[(arg ? arg : 1) - 1])
test = 1;
}
@@ -920,6 +920,7 @@ addbufspc(int need)
if(need & 255)
need = (need | 255) + 1;
bv->buf = realloc(bv->buf, bv->bufspc += need);
+ memset(bv->buf + bv->bufspc - need, 0, need);
bv->bp = bv->buf + bo;
if(bo1 != -1)
bv->bp1 = bv->buf + bo1;
diff --git a/Src/signals.c b/Src/signals.c
index 9e05add09..cad40f4eb 100644
--- a/Src/signals.c
+++ b/Src/signals.c
@@ -55,6 +55,11 @@ mod_export Eprog siglists[VSIGCOUNT];
/**/
mod_export int nsigtrapped;
+/* Running an exit trap? */
+
+/**/
+int in_exit_trap;
+
/*
* Flag that exit trap has been set in POSIX mode.
* The setter's expectation is therefore that it is run
@@ -522,6 +527,11 @@ wait_for_processes(void)
#if defined(HAVE_WAIT3) && defined(HAVE_GETRUSAGE)
struct timezone dummy_tz;
gettimeofday(&pn->endtime, &dummy_tz);
+#ifdef WIFCONTINUED
+ if (WIFCONTINUED(status))
+ pn->status = SP_RUNNING;
+ else
+#endif
pn->status = status;
pn->ti = ru;
#else
@@ -811,7 +821,11 @@ dosavetrap(int sig, int level)
newshf->node.nam = ztrdup(shf->node.nam);
newshf->node.flags = shf->node.flags;
newshf->funcdef = dupeprog(shf->funcdef, 0);
- newshf->filename = ztrdup(shf->filename);
+ if (shf->node.flags & PM_LOADDIR) {
+ dircache_set(&newshf->filename, shf->filename);
+ } else {
+ newshf->filename = ztrdup(shf->filename);
+ }
if (shf->sticky) {
newshf->sticky = sticky_emulation_dup(shf->sticky, 0);
} else
@@ -1426,7 +1440,13 @@ dotrap(int sig)
dont_queue_signals();
+ if (sig == SIGEXIT)
+ ++in_exit_trap;
+
dotrapargs(sig, sigtrapped+sig, funcprog);
+ if (sig == SIGEXIT)
+ --in_exit_trap;
+
restore_queue_signals(q);
}
diff --git a/Src/string.c b/Src/string.c
index a8da14fe0..9e14ef949 100644
--- a/Src/string.c
+++ b/Src/string.c
@@ -52,7 +52,8 @@ dupstring_wlen(const char *s, unsigned len)
if (!s)
return NULL;
t = (char *) zhalloc(len + 1);
- strcpy(t, s);
+ memcpy(t, s, len);
+ t[len] = '\0';
return t;
}
diff --git a/Src/subst.c b/Src/subst.c
index 64b440027..5b1bf8988 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -446,7 +446,7 @@ singsub(char **s)
* NULL to use IFS). The return value is true iff the expansion resulted
* in an empty list.
*
- * *ms_flags is set to bits in the enum above as neeed.
+ * *ms_flags is set to bits in the enum above as needed.
*/
/**/
@@ -481,6 +481,8 @@ multsub(char **s, int pf_flags, char ***a, int *isarr, char *sep,
for ( ; *x; x += l) {
int rawc = -1;
convchar_t c;
+ if (*x == Dash)
+ *x = '-';
if (itok(STOUC(*x))) {
/* token, can't be separator, must be single byte */
rawc = *x;
@@ -622,7 +624,7 @@ filesub(char **namptr, int assign)
char *
equalsubstr(char *str, int assign, int nomatch)
{
- char *pp, *cnam, *cmdstr, *ret;
+ char *pp, *cnam, *cmdstr;
for (pp = str; !isend2(*pp); pp++)
;
@@ -634,10 +636,10 @@ equalsubstr(char *str, int assign, int nomatch)
zerr("%s not found", cmdstr);
return NULL;
}
- ret = dupstring(cnam);
if (*pp)
- ret = dyncat(ret, pp);
- return ret;
+ return dyncat(cnam, pp);
+ else
+ return cnam; /* already duplicated */
}
/**/
@@ -1766,7 +1768,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
*/
c = *s;
if (itype_end(s, IIDENT, 1) == s && *s != '#' && c != Pound &&
- c != '-' && c != '!' && c != '$' && c != String && c != Qstring &&
+ !IS_DASH(c) &&
+ c != '!' && c != '$' && c != String && c != Qstring &&
c != '?' && c != Quest &&
c != '*' && c != Star && c != '@' && c != '{' &&
c != Inbrace && c != '=' && c != Equals && c != Hat &&
@@ -1895,13 +1898,13 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
if (quotetype == QT_DOLLARS ||
quotetype == QT_BACKSLASH_PATTERN)
goto flagerr;
- if (s[1] == '-' || s[1] == '+') {
+ if (IS_DASH(s[1]) || s[1] == '+') {
if (quotemod)
goto flagerr;
s++;
quotemod = 1;
- quotetype = (*s == '-') ? QT_SINGLE_OPTIONAL :
- QT_QUOTEDZPUTS;
+ quotetype = (*s == '+') ? QT_QUOTEDZPUTS :
+ QT_SINGLE_OPTIONAL;
} else {
if (quotetype == QT_SINGLE_OPTIONAL) {
/* extra q's after '-' not allowed */
@@ -2208,9 +2211,9 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
* properly in the first place we wouldn't
* have this nonsense.
*/
- || ((cc == '#' || cc == Pound) &&
- s[2] == Outbrace)
- || cc == '-' || (cc == ':' && s[2] == '-')
+ || ((cc == '#' || cc == Pound) && s[2] == Outbrace)
+ || IS_DASH(cc)
+ || (cc == ':' && IS_DASH(s[2]))
|| (isstring(cc) && (s[2] == Inbrace || s[2] == Inpar)))) {
getlen = 1 + whichlen, s++;
/*
@@ -2605,14 +2608,17 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
* Again, this duplicates tests for characters we're about to
* examine properly later on.
*/
- if (inbrace &&
- (c = *s) != '-' && c != '+' && c != ':' && c != '%' && c != '/' &&
- c != '=' && c != Equals &&
- c != '#' && c != Pound &&
- c != '?' && c != Quest &&
- c != '}' && c != Outbrace) {
- zerr("bad substitution");
- return NULL;
+ if (inbrace) {
+ c = *s;
+ if (!IS_DASH(c) &&
+ c != '+' && c != ':' && c != '%' && c != '/' &&
+ c != '=' && c != Equals &&
+ c != '#' && c != Pound &&
+ c != '?' && c != Quest &&
+ c != '}' && c != Outbrace) {
+ zerr("bad substitution");
+ return NULL;
+ }
}
/*
* Join arrays up if we're in quotes and there isn't some
@@ -2690,8 +2696,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
/* Check for ${..?..} or ${..=..} or one of those. *
* Only works if the name is in braces. */
- if (inbrace && ((c = *s) == '-' ||
- c == '+' ||
+ if (inbrace && ((c = *s) == '+' ||
+ IS_DASH(c) ||
c == ':' || /* i.e. a doubled colon */
c == '=' || c == Equals ||
c == '%' ||
@@ -2802,6 +2808,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
vunset = 1;
/* Fall Through! */
case '-':
+ case Dash:
if (vunset) {
int split_flags;
val = dupstring(s);
@@ -2902,6 +2909,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
} else
setaparam(idbeg, a);
isarr = 1;
+ arrasg = 0;
} else {
untokenize(val);
setsparam(idbeg, ztrdup(val));
@@ -3066,7 +3074,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
if (sval)
zip = hmkarray(sval);
}
- if (!isarr) aval = mkarray(val);
+ if (!isarr) {
+ aval = mkarray(val);
+ isarr = 1;
+ }
if (zip) {
char **out;
int alen, ziplen, outlen, i = 0;
@@ -3089,7 +3100,6 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
out[i*2] = NULL;
aval = out;
copied = 1;
- isarr = 1;
}
} else {
if (unset(UNSET)) {
@@ -3473,8 +3483,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
if (nojoin == 0 || sep) {
val = sepjoin(aval, sep, 1);
isarr = 0;
- ms_flags = 0;
- } else if (force_split && (spsep || nojoin == 2)) {
+ } else if (force_split &&
+ (spsep || nojoin == 2 || (!ifs && isarr < 0))) {
/* Hack to simulate splitting individual elements:
* forced joining as previously determined, or
* join on what we later use to forcibly split
@@ -3482,6 +3492,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
val = sepjoin(aval, (nojoin == 1 ? NULL : spsep), 1);
isarr = 0;
}
+ if (!isarr)
+ ms_flags = 0;
}
if (force_split && !isarr) {
aval = sepsplit(val, spsep, 0, 1);
@@ -3767,6 +3779,13 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
* as a scalar.)
*/
+ if (isarr && ssub) {
+ /* prefork() wants a scalar, so join no matter what else */
+ val = sepjoin(aval, NULL, 1);
+ isarr = 0;
+ l->list.flags &= ~LF_ARRAY;
+ }
+
/*
* If a multsub result had whitespace at the start and we're
* splitting and there's a previous string, now's the time to do so.
@@ -3780,6 +3799,16 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
insertlinknode(l, n, dupstring(fstr)); /* appended, no incnode */
*fstr = '\0';
}
+ if (arrasg && !isarr) {
+ /*
+ * Caller requested this be forced to an array even if scalar.
+ * Any point in distinguishing arrasg == 2 (assoc array) here?
+ */
+ l->list.flags |= LF_ARRAY;
+ aval = hmkarray(val);
+ isarr = 1;
+ DPUTS(!val, "value is NULL in paramsubst, empty array");
+ }
if (isarr) {
char *x;
char *y;
@@ -4302,7 +4331,11 @@ modify(char **str, char **ptr)
break;
case 'P':
if (*copy != '/') {
- copy = zhtricat(metafy(zgetcwd(), -1, META_HEAPDUP), "/", copy);
+ char *here = zgetcwd();
+ if (here[strlen(here)-1] != '/')
+ copy = zhtricat(metafy(here, -1, META_HEAPDUP), "/", copy);
+ else
+ copy = dyncat(here, copy);
}
copy = xsymlink(copy, 1);
break;
@@ -4384,7 +4417,11 @@ modify(char **str, char **ptr)
break;
case 'P':
if (**str != '/') {
- *str = zhtricat(metafy(zgetcwd(), -1, META_HEAPDUP), "/", *str);
+ char *here = zgetcwd();
+ if (here[strlen(here)-1] != '/')
+ *str = zhtricat(metafy(here, -1, META_HEAPDUP), "/", *str);
+ else
+ *str = dyncat(here, *str);
}
*str = xsymlink(*str, 1);
break;
diff --git a/Src/utils.c b/Src/utils.c
index 7f3ddad40..5055d69fe 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -629,7 +629,7 @@ wcs_nicechar_sel(wchar_t c, size_t *widthp, char **swidep, int quotable)
}
s = buf;
- if (!iswprint(c) && (c < 0x80 || !isset(PRINTEIGHTBIT))) {
+ if (!WC_ISPRINT(c) && (c < 0x80 || !isset(PRINTEIGHTBIT))) {
if (c == 0x7f) {
if (quotable) {
*s++ = '\\';
@@ -734,7 +734,7 @@ wcs_nicechar(wchar_t c, size_t *widthp, char **swidep)
/**/
mod_export int is_wcs_nicechar(wchar_t c)
{
- if (!iswprint(c) && (c < 0x80 || !isset(PRINTEIGHTBIT))) {
+ if (!WC_ISPRINT(c) && (c < 0x80 || !isset(PRINTEIGHTBIT))) {
if (c == 0x7f || c == L'\n' || c == L'\t' || c < 0x20)
return 1;
if (c >= 0x80) {
@@ -886,7 +886,7 @@ xsymlinks(char *s, int full)
char **pp, **opp;
char xbuf2[PATH_MAX*3+1], xbuf3[PATH_MAX*2+1];
int t0, ret = 0;
- zulong xbuflen = strlen(xbuf);
+ zulong xbuflen = strlen(xbuf), pplen;
opp = pp = slashsplit(s);
for (; xbuflen < sizeof(xbuf) && *pp && ret >= 0; pp++) {
@@ -907,10 +907,18 @@ xsymlinks(char *s, int full)
xbuflen--;
continue;
}
- sprintf(xbuf2, "%s/%s", xbuf, *pp);
+ /* Includes null byte. */
+ pplen = strlen(*pp) + 1;
+ if (xbuflen + pplen + 1 > sizeof(xbuf2)) {
+ *xbuf = 0;
+ ret = -1;
+ break;
+ }
+ memcpy(xbuf2, xbuf, xbuflen);
+ xbuf2[xbuflen] = '/';
+ memcpy(xbuf2 + xbuflen + 1, *pp, pplen);
t0 = readlink(unmeta(xbuf2), xbuf3, PATH_MAX);
if (t0 == -1) {
- zulong pplen = strlen(*pp) + 1;
if ((xbuflen += pplen) < sizeof(xbuf)) {
strcat(xbuf, "/");
strcat(xbuf, *pp);
@@ -1230,13 +1238,13 @@ adduserdir(char *s, char *t, int flags, int always)
* named directory, since these are sometimes used for
* special purposes.
*/
- nd->dir = ztrdup(t);
+ nd->dir = metafy(t, -1, META_DUP);
} else
- nd->dir = ztrduppfx(t, eptr - t);
+ nd->dir = metafy(t, eptr - t, META_DUP);
/* The variables PWD and OLDPWD are not to be displayed as ~PWD etc. */
if (!strcmp(s, "PWD") || !strcmp(s, "OLDPWD"))
nd->node.flags |= ND_NOABBREV;
- nameddirtab->addnode(nameddirtab, ztrdup(s), nd);
+ nameddirtab->addnode(nameddirtab, metafy(s, -1, META_DUP), nd);
}
/* Get a named directory: this function can cause a directory name *
@@ -2376,7 +2384,7 @@ zstrtol_underscore(const char *s, char **t, int base, int underscore)
while (inblank(*s))
s++;
- if ((neg = (*s == '-')))
+ if ((neg = IS_DASH(*s)))
s++;
else if (*s == '+')
s++;
@@ -4788,6 +4796,41 @@ unmeta(const char *file_name)
}
/*
+ * Unmetafy just one character and store the number of bytes it occupied.
+ */
+/**/
+mod_export convchar_t
+unmeta_one(const char *in, int *sz)
+{
+ convchar_t wc;
+ int newsz;
+#ifdef MULTIBYTE_SUPPORT
+ mbstate_t wstate;
+#endif
+
+ if (!sz)
+ sz = &newsz;
+ *sz = 0;
+
+ if (!in || !*in)
+ return 0;
+
+#ifdef MULTIBYTE_SUPPORT
+ memset(&wstate, 0, sizeof(wstate));
+ *sz = mb_metacharlenconv_r(in, &wc, &wstate);
+#else
+ if (in[0] == Meta) {
+ *sz = 2;
+ wc = STOUC(in[1] ^ 32);
+ } else {
+ *sz = 1;
+ wc = STOUC(in[0]);
+ }
+#endif
+ return wc;
+}
+
+/*
* Unmetafy and compare two strings, comparing unsigned character values.
* "a\0" sorts after "a".
*
@@ -5076,7 +5119,7 @@ niceztrlen(char const *s)
* If flags contains NICEFLAG_HEAP, use the heap for *outstrp, else
* zalloc.
* If flags contsins NICEFLAG_QUOTE, the output is going to be within
- * $'...', so quote "'" with a backslash.
+ * $'...', so quote "'" and "\" with a backslash.
*/
/**/
@@ -5132,6 +5175,10 @@ mb_niceformat(const char *s, FILE *stream, char **outstrp, int flags)
fmt = "\\'";
newl = 2;
}
+ else if (c == L'\\' && (flags & NICEFLAG_QUOTE)) {
+ fmt = "\\\\";
+ newl = 2;
+ }
else
fmt = wcs_nicechar_sel(c, &newl, NULL, flags & NICEFLAG_QUOTE);
break;
@@ -5374,7 +5421,7 @@ mb_metastrlenend(char *ptr, int width, char *eptr)
int num, num_in_char, complete;
if (!isset(MULTIBYTE))
- return ztrlen(ptr);
+ return eptr ? (int)(eptr - ptr) : ztrlen(ptr);
laststart = ptr;
ret = MB_INVALID;
@@ -6118,7 +6165,9 @@ quotedzputs(char const *s, FILE *stream)
} else
*ptr++ = '\'';
while(*s) {
- if (*s == Meta)
+ if (*s == Dash)
+ c = '-';
+ else if (*s == Meta)
c = *++s ^ 32;
else
c = *s;
@@ -6155,7 +6204,9 @@ quotedzputs(char const *s, FILE *stream)
} else {
/* use Bourne-style quoting, avoiding empty quoted strings */
while (*s) {
- if (*s == Meta)
+ if (*s == Dash)
+ c = '-';
+ else if (*s == Meta)
c = *++s ^ 32;
else
c = *s;
diff --git a/Src/watch.c b/Src/watch.c
index c804913ad..cd7dc643d 100644
--- a/Src/watch.c
+++ b/Src/watch.c
@@ -87,6 +87,15 @@
#if !defined(WATCH_STRUCT_UTMP) && defined(HAVE_STRUCT_UTMPX) && defined(REAL_UTMPX_FILE)
# define WATCH_STRUCT_UTMP struct utmpx
+# if defined(HAVE_SETUTXENT) && defined(HAVE_GETUTXENT) && defined(HAVE_ENDUTXENT)
+# define setutent setutxent
+# define getutent getutxent
+# define endutent endutxent
+# ifndef HAVE_GETUTENT
+# define HAVE_GETUTENT 1
+# endif
+# endif
+
/*
* In utmpx, the ut_name field is replaced by ut_user.
* Howver, on some systems ut_name may already be defined this
@@ -141,9 +150,9 @@ char const * const default_watchfmt = DEFAULT_WATCHFMT;
# define WATCH_WTMP_FILE "/dev/null"
# endif
-static int wtabsz;
-static WATCH_STRUCT_UTMP *wtab;
-static time_t lastutmpcheck;
+static int wtabsz = 0;
+static WATCH_STRUCT_UTMP *wtab = NULL;
+static time_t lastutmpcheck = 0;
/* get the time of login/logout for WATCH */
@@ -473,34 +482,60 @@ ucmp(WATCH_STRUCT_UTMP *u, WATCH_STRUCT_UTMP *v)
/* initialize the user List */
/**/
-static void
-readwtab(void)
+static int
+readwtab(WATCH_STRUCT_UTMP **head, int initial_sz)
{
WATCH_STRUCT_UTMP *uptr;
- int wtabmax = 32;
+ int wtabmax = initial_sz < 2 ? 32 : initial_sz;
+ int sz = 0;
+# ifdef HAVE_GETUTENT
+ WATCH_STRUCT_UTMP *tmp;
+# else
FILE *in;
+# endif
- wtabsz = 0;
+ uptr = *head = (WATCH_STRUCT_UTMP *)
+ zalloc(wtabmax * sizeof(WATCH_STRUCT_UTMP));
+# ifdef HAVE_GETUTENT
+ setutent();
+ while ((tmp = getutent()) != NULL) {
+ memcpy(uptr, tmp, sizeof (WATCH_STRUCT_UTMP));
+# else
if (!(in = fopen(WATCH_UTMP_FILE, "r")))
- return;
- uptr = wtab = (WATCH_STRUCT_UTMP *)zalloc(wtabmax * sizeof(WATCH_STRUCT_UTMP));
- while (fread(uptr, sizeof(WATCH_STRUCT_UTMP), 1, in))
+ return 0;
+ while (fread(uptr, sizeof(WATCH_STRUCT_UTMP), 1, in)) {
+# endif
# ifdef USER_PROCESS
- if (uptr->ut_type == USER_PROCESS)
+ if (uptr->ut_type == USER_PROCESS)
# else /* !USER_PROCESS */
- if (uptr->ut_name[0])
+ if (uptr->ut_name[0])
# endif /* !USER_PROCESS */
{
uptr++;
- if (++wtabsz == wtabmax)
- uptr = (wtab = (WATCH_STRUCT_UTMP *)realloc((void *) wtab, (wtabmax *= 2) *
- sizeof(WATCH_STRUCT_UTMP))) + wtabsz;
+ if (++sz == wtabmax) {
+ uptr = (WATCH_STRUCT_UTMP *)
+ realloc(*head, (wtabmax *= 2) * sizeof(WATCH_STRUCT_UTMP));
+ if (uptr == NULL) {
+ /* memory pressure - so stop consuming and use, what we have
+ * Other option is to exit() here, as zmalloc does on error */
+ sz--;
+ break;
+ }
+ *head = uptr;
+ uptr += sz;
+ }
}
+ }
+# ifdef HAVE_GETUTENT
+ endutent();
+# else
fclose(in);
+# endif
- if (wtabsz)
- qsort((void *) wtab, wtabsz, sizeof(WATCH_STRUCT_UTMP),
+ if (sz)
+ qsort((void *) *head, sz, sizeof(WATCH_STRUCT_UTMP),
(int (*) _((const void *, const void *)))ucmp);
+ return sz;
}
/* Check for login/logout events; executed before *
@@ -510,55 +545,28 @@ readwtab(void)
void
dowatch(void)
{
- FILE *in;
WATCH_STRUCT_UTMP *utab, *uptr, *wptr;
struct stat st;
char **s;
char *fmt;
- int utabsz = 0, utabmax = wtabsz + 4;
- int uct, wct;
+ int utabsz, uct, wct;
s = watch;
holdintr();
- if (!wtab) {
- readwtab();
- noholdintr();
- return;
- }
+ if (!wtab)
+ wtabsz = readwtab(&wtab, 32);
if ((stat(WATCH_UTMP_FILE, &st) == -1) || (st.st_mtime <= lastutmpcheck)) {
noholdintr();
return;
}
lastutmpcheck = st.st_mtime;
- uptr = utab = (WATCH_STRUCT_UTMP *) zalloc(utabmax * sizeof(WATCH_STRUCT_UTMP));
-
- if (!(in = fopen(WATCH_UTMP_FILE, "r"))) {
- free(utab);
- noholdintr();
- return;
- }
- while (fread(uptr, sizeof *uptr, 1, in))
-# ifdef USER_PROCESS
- if (uptr->ut_type == USER_PROCESS)
-# else /* !USER_PROCESS */
- if (uptr->ut_name[0])
-# endif /* !USER_PROCESS */
- {
- uptr++;
- if (++utabsz == utabmax)
- uptr = (utab = (WATCH_STRUCT_UTMP *)realloc((void *) utab, (utabmax *= 2) *
- sizeof(WATCH_STRUCT_UTMP))) + utabsz;
- }
- fclose(in);
+ utabsz = readwtab(&utab, wtabsz + 4);
noholdintr();
if (errflag) {
free(utab);
return;
}
- if (utabsz)
- qsort((void *) utab, utabsz, sizeof(WATCH_STRUCT_UTMP),
- (int (*) _((const void *, const void *)))ucmp);
wct = wtabsz;
uct = utabsz;
@@ -571,13 +579,14 @@ dowatch(void)
queue_signals();
if (!(fmt = getsparam_u("WATCHFMT")))
fmt = DEFAULT_WATCHFMT;
- while ((uct || wct) && !errflag)
+ while ((uct || wct) && !errflag) {
if (!uct || (wct && ucmp(uptr, wptr) > 0))
wct--, watchlog(0, wptr++, s, fmt);
else if (!wct || (uct && ucmp(uptr, wptr) < 0))
uct--, watchlog(1, uptr++, s, fmt);
else
uptr++, wptr++, wct--, uct--;
+ }
unqueue_signals();
free(wtab);
wtab = utab;
diff --git a/Src/wcwidth9.h b/Src/wcwidth9.h
index 07e6bae1c..448f548e9 100644
--- a/Src/wcwidth9.h
+++ b/Src/wcwidth9.h
@@ -22,6 +22,7 @@ static const struct wcwidth9_interval wcwidth9_nonprint[] = {
{0x070f, 0x070f},
{0x180b, 0x180e},
{0x200b, 0x200f},
+ {0x2028, 0x2029},
{0x202a, 0x202e},
{0x206a, 0x206f},
{0xd800, 0xdfff},
@@ -1283,6 +1284,9 @@ static inline bool wcwidth9_intable(const struct wcwidth9_interval *table, size_
}
static inline int wcwidth9(int c) {
+ if (c == 0) {
+ return 0;
+ }
if (c < 0|| c > 0x10ffff) {
return -1;
}
@@ -1292,7 +1296,7 @@ static inline int wcwidth9(int c) {
}
if (wcwidth9_intable(wcwidth9_combining, WCWIDTH9_ARRAY_SIZE(wcwidth9_combining), c)) {
- return -1;
+ return 0;
}
if (wcwidth9_intable(wcwidth9_not_assigned, WCWIDTH9_ARRAY_SIZE(wcwidth9_not_assigned), c)) {
diff --git a/Src/zsh.h b/Src/zsh.h
index f22d8b135..ccd11db3d 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -238,6 +238,16 @@ struct mathfunc {
#define PATCHARS "#^*()|[]<>?~\\"
/*
+ * Check for a possibly tokenized dash.
+ *
+ * A dash only needs to be a token in a character range, [a-z], but
+ * it's difficult in general to ensure that. So it's turned into
+ * a token at the usual point in the lexer. However, we need
+ * to check for a literal dash at many points.
+ */
+#define IS_DASH(x) ((x) == '-' || (x) == Dash)
+
+/*
* Types of quote. This is used in various places, so care needs
* to be taken when changing them. (Oooh, don't you look surprised.)
* - Passed to quotestring() to indicate style. This is the ultimate
@@ -803,6 +813,7 @@ struct eccstr {
char *str;
wordcode offs, aoffs;
int nfunc;
+ int hashval;
};
#define EC_NODUP 0
@@ -1019,6 +1030,7 @@ struct job {
#define STAT_BUILTIN (0x4000) /* job at tail of pipeline is a builtin */
#define STAT_SUBJOB_ORPHANED (0x8000)
/* STAT_SUBJOB with STAT_SUPERJOB exited */
+#define STAT_DISOWN (0x10000) /* STAT_SUPERJOB with disown pending */
#define SP_RUNNING -1 /* fake status for jobs currently running */
@@ -1233,7 +1245,9 @@ struct cmdnam {
struct shfunc {
struct hashnode node;
- char *filename; /* Name of file located in */
+ char *filename; /* Name of file located in.
+ For not yet autoloaded file, name
+ of explicit directory, if not NULL. */
zlong lineno; /* line number in above file */
Eprog funcdef; /* function definition */
Eprog redir; /* redirections to apply */
@@ -1529,6 +1543,7 @@ struct patstralloc {
/* Flags used in pattern matchers (Patprog) and passed down to patcompile */
+#define PAT_HEAPDUP 0x0000 /* Dummy flag for default behavior */
#define PAT_FILE 0x0001 /* Pattern is a file name */
#define PAT_FILET 0x0002 /* Pattern is top level file, affects ~ */
#define PAT_ANY 0x0004 /* Match anything (cheap "*") */
@@ -1804,6 +1819,7 @@ struct tieddata {
#define PM_READONLY (1<<10) /* readonly */
#define PM_TAGGED (1<<11) /* tagged */
#define PM_EXPORTED (1<<12) /* exported */
+#define PM_ABSPATH_USED (1<<12) /* (function): loaded using absolute path */
/* The following are the same since they *
* both represent -U option to typeset */
@@ -1811,7 +1827,9 @@ struct tieddata {
#define PM_UNALIASED (1<<13) /* do not expand aliases when autoloading */
#define PM_HIDE (1<<14) /* Special behaviour hidden by local */
+#define PM_CUR_FPATH (1<<14) /* (function): can use $fpath with filename */
#define PM_HIDEVAL (1<<15) /* Value not shown in `typeset' commands */
+#define PM_WARNNESTED (1<<15) /* (function): non-recursive WARNNESTEDVAR */
#define PM_TIED (1<<16) /* array tied to colon-path or v.v. */
#define PM_TAGGED_LOCAL (1<<16) /* (function): non-recursive PM_TAGGED */
@@ -1820,6 +1838,7 @@ struct tieddata {
/* Remaining flags do not correspond directly to command line arguments */
#define PM_DONTIMPORT_SUID (1<<19) /* do not import if running setuid */
+#define PM_LOADDIR (1<<19) /* (function) filename gives load directory */
#define PM_SINGLE (1<<20) /* special can only have a single instance */
#define PM_LOCAL (1<<21) /* this parameter will be made local */
#define PM_SPECIAL (1<<22) /* special builtin parameter */
@@ -2012,9 +2031,15 @@ struct paramdef {
* Flags for assignsparam and assignaparam.
*/
enum {
+ /* Add to rather than override value */
ASSPM_AUGMENT = 1 << 0,
+ /* Test for warning if creating global variable in function */
ASSPM_WARN_CREATE = 1 << 1,
- ASSPM_ENV_IMPORT = 1 << 2
+ /* Test for warning if using nested variable in function */
+ ASSPM_WARN_NESTED = 1 << 2,
+ ASSPM_WARN = (ASSPM_WARN_CREATE|ASSPM_WARN_NESTED),
+ /* Import from environment, so exercise care evaluating value */
+ ASSPM_ENV_IMPORT = 1 << 3,
};
/* node for named directory hash table (nameddirtab) */
@@ -2222,6 +2247,7 @@ struct histent {
enum {
OPT_INVALID,
ALIASESOPT,
+ ALIASFUNCDEF,
ALLEXPORT,
ALWAYSLASTPROMPT,
ALWAYSTOEND,
@@ -2395,6 +2421,7 @@ enum {
VERBOSE,
VIMODE,
WARNCREATEGLOBAL,
+ WARNNESTEDVAR,
XTRACE,
USEZLE,
DVORAK,
@@ -2893,6 +2920,7 @@ struct hist_stack {
int histdone;
int stophist;
int hlinesz;
+ zlong defev;
char *hline;
char *hptr;
short *chwords;
@@ -3133,9 +3161,7 @@ typedef wint_t convchar_t;
* works on MacOS which doesn't define that.
*/
#ifdef ENABLE_UNICODE9
-#define WCWIDTH(wc) mk_wcwidth(wc)
-#elif defined(BROKEN_WCWIDTH) && (defined(__STDC_ISO_10646__) || defined(__APPLE__))
-#define WCWIDTH(wc) mk_wcwidth(wc)
+#define WCWIDTH(wc) u9_wcwidth(wc)
#else
#define WCWIDTH(wc) wcwidth(wc)
#endif
@@ -3180,15 +3206,7 @@ typedef wint_t convchar_t;
* sense throughout the shell. I am not aware of a way of
* detecting the Unicode trait in standard libraries.
*/
-#ifdef BROKEN_WCWIDTH
-/*
- * We can't be quite sure the wcwidth we've provided is entirely
- * in agreement with the system's, so be extra safe.
- */
-#define IS_COMBINING(wc) (wc != 0 && WCWIDTH(wc) == 0 && !iswcntrl(wc))
-#else
#define IS_COMBINING(wc) (wc != 0 && WCWIDTH(wc) == 0)
-#endif
/*
* Test for the base of a combining character.
*
diff --git a/Src/ztype.h b/Src/ztype.h
index 76589b152..ae7236774 100644
--- a/Src/ztype.h
+++ b/Src/ztype.h
@@ -72,7 +72,11 @@
#ifdef MULTIBYTE_SUPPORT
#define WC_ZISTYPE(X,Y) wcsitype((X),(Y))
-#define WC_ISPRINT(X) iswprint(X)
+# ifdef ENABLE_UNICODE9
+# define WC_ISPRINT(X) u9_iswprint(X)
+# else
+# define WC_ISPRINT(X) iswprint(X)
+# endif
#else
#define WC_ZISTYPE(X,Y) zistype((X),(Y))
#define WC_ISPRINT(X) isprint(X)