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"