Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: Anyone want to help make zsh/db/gdbm work?



On Mon, 26 Jan 2015 12:11:16 +0000
Peter Stephenson <p.w.stephenson@xxxxxxxxxxxx> wrote:
> typeset GDBMB-foo
> (){
>   local GDBMDB
>   unset GDBMDB
>   ztie -d db/gdbm -f gdbmdb GDBMDB
>   zuntie GDBMDB
> }
> print $GDBMB
> 
> Note there's now a zuntie there.  This still doesn't appear to work.
> It now does without the zuntie, though, at least for the most obvious
> limited value of "work".

That should have been an "=" in the top line.  I think that was webmail
again.  (Certainly not not my fault, I wouldn't make misteaks in emall.)

I tried this again and it was differently screwed up from what I
expected.  However, to cut a long story short, I think the following
fixes all the cases I know about.  It may fix a leak with zuntie:  I
don't think it was freeing the parameter it removed from the table.
Turning it into a standard unset should in principle make it more
foolproof.

I've also made zuntie safer: it checks the parameter was indeed tied
before.

The fix in module.c would affect special hashes loaded by the parameter
module.  However, in that case it was highly atypical for the special to 
hide an existing local variable so it's much less likely to have been
seen.

Could do with playing with, tests, etc., but I think I'll leave it at
this.

diff --git a/Src/Modules/db_gdbm.c b/Src/Modules/db_gdbm.c
index d75af98..a6027de 100644
--- a/Src/Modules/db_gdbm.c
+++ b/Src/Modules/db_gdbm.c
@@ -105,7 +105,6 @@ static int
 bin_zuntie(char *nam, char **args, Options ops, UNUSED(int func))
 {
     Param pm;
-    GDBM_FILE dbf;
     char *pmname;
     int ret = 0;
 
@@ -116,12 +115,18 @@ bin_zuntie(char *nam, char **args, Options ops, UNUSED(int func))
 	    ret = 1;
 	    continue;
 	}
+	if (pm->gsu.h != &gdbm_hash_gsu) {
+	    zwarnnam(nam, "not a tied gdbm hash: %s", pmname);
+	    ret = 1;
+	    continue;
+	}
 
-	dbf = (GDBM_FILE)(pm->u.hash->tmpdata);
-	gdbm_close(dbf);
-	/* free(pm->u.hash->tmpdata); */
-	pm->u.hash->tmpdata = NULL;
-	paramtab->removenode(paramtab, pm->node.nam);
+	queue_signals();
+	if (unsetparam_pm(pm, 0, 1)) {
+	    /* assume already reported */
+	    ret = 1;
+	}
+	unqueue_signals();
     }
 
     return ret;
diff --git a/Src/params.c b/Src/params.c
index 64c78bd..e8a9010 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -927,7 +927,15 @@ shempty(void)
 {
 }
 
-/* Create a simple special hash parameter. */
+/*
+ * Create a simple special hash parameter.
+ *
+ * This is for hashes added internally --- it's not possible to add
+ * special hashes from shell commands.  It's currently used
+ * - by addparamdef() for special parameters in the zsh/parameter
+ *   module
+ * - by ztie for special parameters tied to databases.
+ */
 
 /**/
 mod_export Param
@@ -939,7 +947,22 @@ createspecialhash(char *name, GetNodeFunc get, ScanTabFunc scan, int flags)
     if (!(pm = createparam(name, PM_SPECIAL|PM_HASHED|flags)))
 	return NULL;
 
-    pm->level = pm->old ? locallevel : 0;
+    /*
+     * If there's an old parameter, we'll put the new one at
+     * the current locallevel, so that the old parameter is
+     * exposed again after leaving the function.  Otherwise,
+     * we'll leave it alone.  Usually this means the parameter
+     * will stay in place until explicitly unloaded, however
+     * if the parameter was previously unset within a function
+     * we'll inherit the level of that function and follow the
+     * standard convention that the parameter remains local
+     * even if unset.
+     *
+     * These semantics are similar to those of a normal parameter set
+     * within a function without a local definition.
+     */
+    if (pm->old)
+	pm->level = locallevel;
     pm->gsu.h = (flags & PM_READONLY) ? &stdhash_gsu :
 	&nullsethash_gsu;
     pm->u.hash = ht = newhashtable(0, name, NULL);



Messages sorted by: Reverse Date, Date, Thread, Author