summaryrefslogtreecommitdiff
path: root/Src/Modules
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Modules')
-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
13 files changed, 794 insertions, 156 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;