Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
[PATCH 2/2] persistent locals - zsh/param/persistent module
- X-seq: zsh-workers 52118
- From: Bart Schaefer <schaefer@xxxxxxxxxxxxxxxx>
- To: Zsh hackers list <zsh-workers@xxxxxxx>
- Subject: [PATCH 2/2] persistent locals - zsh/param/persistent module
- Date: Tue, 5 Sep 2023 22:05:11 -0700
- Archived-at: <https://zsh.org/workers/52118>
- List-id: <zsh-workers.zsh.org>
diff --git a/Src/Modules/param_persistent.c b/Src/Modules/param_persistent.c
new file mode 100644
index 000000000..728559fc9
--- /dev/null
+++ b/Src/Modules/param_persistent.c
@@ -0,0 +1,219 @@
+/*
+ * param_persistent.c - bindings for persistent parameter scopes
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 2015 Barton E. Schaefer
+ * All rights reserved.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and to distribute modified versions of this software for any
+ * purpose, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * In no event shall Barton E. Schaefer or the Zsh Development
+ * Group be liable to any party for direct, indirect, special, incidental, or
+ * consequential damages arising out of the use of this software and its
+ * documentation, even if Barton E. Schaefer and the Zsh
+ * Development Group have been advised of the possibility of such damage.
+ *
+ * Barton E. Schaefer and the Zsh Development Group
+ * specifically disclaim any warranties, including, but not limited to, the
+ * implied warranties of merchantability and fitness for a particular purpose.
+ * The software provided hereunder is on an "as is" basis, and
+ * Barton E. Schaefer and the Zsh Development Group have no
+ * obligation to provide maintenance, support, updates, enhancements, or
+ * modifications.
+ *
+ */
+
+#include "param_persistent.mdh"
+#include "param_persistent.pro"
+
+/*
+ * The trick here is:
+ *
+ * bin_persistent() calls makepersistent() on its "args" array. It
+ * is not a keyword, so the "assigns" list should be empty.
+ *
+ * makepersistent() walks the array of arguments and, for each name,
+ * pastes up a ".persistent_FUNC.name" string where FUNC is the name
+ * of the function that invoked "persistent". It tests whether there
+ * is already a parameter having that name. If not, the name in the
+ * assign struct is changed to this name. A new assignment of the
+ * original name to this new name is placed in a new list. The new
+ * list of assignments thus created is returned.
+ *
+ * Assignments for which the persistent name already exists are dropped,
+ * so the parameter retains its flags and value from the previous call.
+ *
+ * bin_persistent() then calls bin_typeset() on the new list with opts
+ * arranged so a to create named references. If this succeeds, it calls
+ * bin_typeset() again on the rewritten array to handle creation of any
+ * actual parameters to which the named references (the original names
+ * to be made persistent) need to point.
+ *
+ * The end result is that after the first call to "persistent" there is
+ * a global variable in a hidden namespace to which each persistent name
+ * is a reference. Thereafter, everything happens via the named reference.
+ */
+
+static int makepersistent_error = 0;
+
+/**/
+static Asgment
+cpyasg(char ***argvp)
+{
+ char *s = **argvp, *v = NULL, *new;
+ Asgment a;
+
+ /* Like builtin.c:getasg() without assigns */
+ if (!s || !*s)
+ return NULL;
+ if (*s == '=') {
+ makepersistent_error = 1;
+ return NULL;
+ }
+
+ a = (Asgment)zhalloc(sizeof(struct asgment));
+ a->name = s;
+ a->flags = 0;
+ new = zhtricat(".persistent_",argzero,".");
+
+ for (; *s && *s != '='; s++);
+ if (*s) {
+ *s = '\0';
+ v = s + 1;
+ }
+ a->value.scalar = new = dyncat(new, **argvp);
+
+ if (is_persistent(new))
+ for (char **argv = *argvp; *argv; argv++)
+ argv[0] = argv[1];
+ else {
+ if (v)
+ **argvp = zhtricat(new, "=", v);
+ else
+ **argvp = dupstring(new);
+ (*argvp)++;
+ }
+ return a;
+}
+
+/**/
+static LinkList
+makepersistent(char **argv)
+{
+ LinkList persist = newlinklist();
+ char ***argvp = &argv;
+ Asgment asg;
+
+ while (**argvp) {
+ if ((asg = cpyasg(argvp)))
+ uaddlinknode(persist, &asg->node);
+ }
+
+ return persist;
+}
+
+/**/
+static int
+is_persistent(const char *name)
+{
+ return (!strncmp(name, ".persistent_", 12) &&
+ !!paramtab->getnode(paramtab, name));
+}
+
+/**/
+static int
+bin_persistent(char *nam, char **args, LinkList assigns, Options ops, int func)
+{
+ int from_typeset = 0;
+ int save_llevel = locallevel;
+ makepersistent_error = 0;
+
+ if (*itype_end(argzero, IIDENT, 0)) {
+ zwarnnam("persistent", "invalid scope: %s", argzero);
+ return 1;
+ }
+
+ LinkList namerefs = makepersistent(args);
+ if (makepersistent_error)
+ return 1;
+ if (*args) {
+ queue_signals();
+ locallevel = 0; /* Does this work? */
+ from_typeset = bin_typeset(nam, args, assigns, ops, func);
+ locallevel = save_llevel;
+ unqueue_signals();
+ }
+ if (!from_typeset) {
+ *args = 0;
+ memset((void *)ops, 0, sizeof(struct options));
+ ops->ind['n'] = ops->ind['H'] = 1;
+ return bin_typeset(nam, args, namerefs, ops, func);
+ }
+
+ return makepersistent_error | from_typeset;
+}
+
+/*
+ * Standard module configuration/linkage
+ */
+
+static struct builtin bintab[] = {
+ /* Copied from BUILTIN("local"), removed "m" and "p" */
+ BUILTIN("persistent", BINF_MAGICEQUALS | BINF_ASSIGN, (HandlerFunc)bin_persistent, 0, -1, 0, "AE:%F:%HL:%PR:%TUZ:%ahi:%lnrtux", NULL)
+};
+
+static struct features module_features = {
+ bintab, sizeof(bintab)/sizeof(*bintab),
+ NULL, 0,
+ NULL, 0,
+ NULL, 0,
+ 0
+};
+
+/**/
+int
+setup_(UNUSED(Module m))
+{
+ return 0;
+}
+
+/**/
+int
+features_(Module m, char ***features)
+{
+ *features = featuresarray(m, &module_features);
+ return 0;
+}
+
+/**/
+int
+enables_(Module m, int **enables)
+{
+ return handlefeatures(m, &module_features, enables);
+}
+
+/**/
+int
+boot_(Module m)
+{
+ return 0;
+}
+
+/**/
+int
+cleanup_(Module m)
+{
+ return setfeatureenables(m, &module_features, NULL);
+}
+
+/**/
+int
+finish_(UNUSED(Module m))
+{
+ return 0;
+}
diff --git a/Src/Modules/param_persistent.mdd b/Src/Modules/param_persistent.mdd
new file mode 100644
index 000000000..8f95451d5
--- /dev/null
+++ b/Src/Modules/param_persistent.mdd
@@ -0,0 +1,7 @@
+name=zsh/param/persistent
+link=dynamic
+load=yes
+
+autofeatures="b:persistent"
+
+objects="param_persistent.o"
Messages sorted by:
Reverse Date,
Date,
Thread,
Author