summaryrefslogtreecommitdiff
path: root/Src/Modules/param_private.c
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Modules/param_private.c')
-rw-r--r--Src/Modules/param_private.c587
1 files changed, 587 insertions, 0 deletions
diff --git a/Src/Modules/param_private.c b/Src/Modules/param_private.c
new file mode 100644
index 000000000..7f9aa7921
--- /dev/null
+++ b/Src/Modules/param_private.c
@@ -0,0 +1,587 @@
+/*
+ * param_private.c - bindings for private 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_private.mdh"
+#include "param_private.pro"
+
+struct gsu_closure {
+ union {
+ struct gsu_scalar s;
+ struct gsu_integer i;
+ struct gsu_float f;
+ struct gsu_array a;
+ struct gsu_hash h;
+ } u;
+ void *g;
+};
+
+const struct gsu_scalar scalar_private_gsu =
+{ pps_getfn, pps_setfn, pps_unsetfn };
+
+const struct gsu_integer integer_private_gsu =
+{ ppi_getfn, ppi_setfn, ppi_unsetfn };
+
+const struct gsu_float float_private_gsu =
+{ ppf_getfn, ppf_setfn, ppf_unsetfn };
+
+const struct gsu_array array_private_gsu =
+{ ppa_getfn, ppa_setfn, ppa_unsetfn };
+
+const struct gsu_hash hash_private_gsu =
+{ pph_getfn, pph_setfn, pph_unsetfn };
+
+/*
+ * The trick here is:
+ *
+ * bin_private() opens a new parameter scope, then calls bin_typeset().
+ *
+ * bin_typeset() handles the usual parameter creation and error checks.
+ *
+ * makeprivate() then finds all parameters created in the new scope and
+ * rejects them if they can't be "promoted" to the surrounding scope.
+ * Otherwise it swaps out their GSU structure and promotes them so they
+ * will be removed when the surrounding scope ends.
+ *
+ * bin_private() then ends the current scope, which discards any of the
+ * parameters rejected by makeprivate().
+ *
+ */
+
+static int makeprivate_error = 0;
+
+static void
+makeprivate(HashNode hn, UNUSED(int flags))
+{
+ Param pm = (Param)hn;
+ if (pm->level == locallevel) {
+ if (pm->ename || (pm->node.flags & PM_NORESTORE) ||
+ (pm->old &&
+ (pm->old->level == locallevel - 1 ||
+ ((pm->node.flags & (PM_SPECIAL|PM_REMOVABLE)) == PM_SPECIAL &&
+ /* typeset_single() line 2300 discards PM_REMOVABLE -- why? */
+ !is_private(pm->old))))) {
+ zwarnnam("private", "can't change scope of existing param: %s",
+ pm->node.nam);
+ makeprivate_error = 1;
+ return;
+ }
+ struct gsu_closure *gsu = zhalloc(sizeof(struct gsu_closure));
+ switch (PM_TYPE(pm->node.flags)) {
+ case PM_SCALAR:
+ gsu->g = (void *)(pm->gsu.s);
+ gsu->u.s = scalar_private_gsu;
+ pm->gsu.s = (GsuScalar)gsu;
+ break;
+ case PM_INTEGER:
+ gsu->g = (void *)(pm->gsu.i);
+ gsu->u.i = integer_private_gsu;
+ pm->gsu.i = (GsuInteger)gsu;
+ break;
+ case PM_EFLOAT:
+ case PM_FFLOAT:
+ gsu->g = (void *)(pm->gsu.f);
+ gsu->u.f = float_private_gsu;
+ pm->gsu.f = (GsuFloat)gsu;
+ break;
+ case PM_ARRAY:
+ gsu->g = (void *)(pm->gsu.a);
+ gsu->u.a = array_private_gsu;
+ pm->gsu.a = (GsuArray)gsu;
+ break;
+ case PM_HASHED:
+ gsu->g = (void *)(pm->gsu.h);
+ gsu->u.h = hash_private_gsu;
+ pm->gsu.h = (GsuHash)gsu;
+ break;
+ default:
+ makeprivate_error = 1;
+ break;
+ }
+ /* PM_HIDE so new parameters in deeper scopes do not shadow */
+ pm->node.flags |= (PM_HIDE|PM_SPECIAL|PM_REMOVABLE);
+ pm->level -= 1;
+ }
+}
+
+/**/
+static int
+is_private(Param pm)
+{
+ switch (PM_TYPE(pm->node.flags)) {
+ case PM_SCALAR:
+ if (!pm->gsu.s || pm->gsu.s->unsetfn != pps_unsetfn)
+ return 0;
+ break;
+ case PM_INTEGER:
+ if (!pm->gsu.i || pm->gsu.i->unsetfn != ppi_unsetfn)
+ return 0;
+ break;
+ case PM_EFLOAT:
+ case PM_FFLOAT:
+ if (!pm->gsu.f || pm->gsu.f->unsetfn != ppf_unsetfn)
+ return 0;
+ break;
+ case PM_ARRAY:
+ if (!pm->gsu.a || pm->gsu.a->unsetfn != ppa_unsetfn)
+ return 0;
+ break;
+ case PM_HASHED:
+ if (!pm->gsu.h || pm->gsu.h->unsetfn != pph_unsetfn)
+ return 0;
+ break;
+ default:
+ /* error */
+ return 0;
+ }
+ return 1;
+}
+
+static int fakelevel;
+
+/**/
+static int
+bin_private(char *nam, char **args, LinkList assigns, Options ops, int func)
+{
+ int from_typeset = 1;
+ makeprivate_error = 0;
+
+ if (!OPT_ISSET(ops, 'P'))
+ return bin_typeset(nam, args, assigns, ops, func);
+ else if (OPT_ISSET(ops, 'T')) {
+ zwarn("bad option: -T");
+ return 1;
+ }
+
+ if (locallevel == 0) {
+ if (isset(WARNCREATEGLOBAL))
+ zwarnnam(nam, "invalid local scope, using globals");
+ return bin_typeset("private", args, assigns, ops, func);
+ }
+
+ ops->ind['g'] = 2; /* force bin_typeset() to behave as "local" */
+
+ queue_signals();
+ fakelevel = locallevel;
+ startparamscope();
+ from_typeset = bin_typeset("private", args, assigns, ops, func);
+ scanhashtable(paramtab, 0, 0, 0, makeprivate, 0);
+ endparamscope();
+ fakelevel = 0;
+ unqueue_signals();
+
+ return makeprivate_error | from_typeset;
+}
+
+static void
+setfn_error(Param pm)
+{
+ pm->node.flags |= PM_UNSET;
+ zerr("%s: attempt to assign private in nested scope", pm->node.nam);
+}
+
+/*
+ * How the GSU functions work:
+ *
+ * The getfn and setfn family compare to locallevel and then call through
+ * to the original getfn or setfn. This means you can't assign at a
+ * deeper scope to any parameter declared private unless you first declare
+ * it local again at the new scope. Testing locallevel in getfn is most
+ * likely unnecessary given the scopeprivate() wrapper installed below.
+ *
+ * The unsetfn family compare locallevel and restore the old GSU before
+ * calling the original unsetfn. This assures that if the old unsetfn
+ * wants to use its getfn or setfn, they're unconditionally present.
+ *
+ */
+
+/**/
+static char *
+pps_getfn(Param pm)
+{
+ struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.s);
+ GsuScalar gsu = (GsuScalar)(c->g);
+
+ if (locallevel >= pm->level)
+ return gsu->getfn(pm);
+ else
+ return (char *) hcalloc(1);
+}
+
+/**/
+static void
+pps_setfn(Param pm, char *x)
+{
+ struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.s);
+ GsuScalar gsu = (GsuScalar)(c->g);
+ if (locallevel == pm->level)
+ gsu->setfn(pm, x);
+ else
+ setfn_error(pm);
+}
+
+/**/
+static void
+pps_unsetfn(Param pm, int x)
+{
+ struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.s);
+ GsuScalar gsu = (GsuScalar)(c->g);
+ pm->gsu.s = gsu;
+ if (locallevel <= pm->level)
+ gsu->unsetfn(pm, x);
+}
+
+/**/
+static zlong
+ppi_getfn(Param pm)
+{
+ struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.i);
+ GsuInteger gsu = (GsuInteger)(c->g);
+ if (locallevel >= pm->level)
+ return gsu->getfn(pm);
+ else
+ return 0;
+}
+
+/**/
+static void
+ppi_setfn(Param pm, zlong x)
+{
+ struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.i);
+ GsuInteger gsu = (GsuInteger)(c->g);
+ if (locallevel == pm->level)
+ gsu->setfn(pm, x);
+ else
+ setfn_error(pm);
+}
+
+/**/
+static void
+ppi_unsetfn(Param pm, int x)
+{
+ struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.i);
+ GsuInteger gsu = (GsuInteger)(c->g);
+ pm->gsu.i = gsu;
+ if (locallevel <= pm->level)
+ gsu->unsetfn(pm, x);
+}
+
+/**/
+static double
+ppf_getfn(Param pm)
+{
+ struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.f);
+ GsuFloat gsu = (GsuFloat)(c->g);
+ if (locallevel >= pm->level)
+ return gsu->getfn(pm);
+ else
+ return 0;
+}
+
+/**/
+static void
+ppf_setfn(Param pm, double x)
+{
+ struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.f);
+ GsuFloat gsu = (GsuFloat)(c->g);
+ if (locallevel == pm->level)
+ gsu->setfn(pm, x);
+ else
+ setfn_error(pm);
+}
+
+/**/
+static void
+ppf_unsetfn(Param pm, int x)
+{
+ struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.f);
+ GsuFloat gsu = (GsuFloat)(c->g);
+ pm->gsu.f = gsu;
+ if (locallevel <= pm->level)
+ gsu->unsetfn(pm, x);
+}
+
+/**/
+static char **
+ppa_getfn(Param pm)
+{
+ static char *nullarray = NULL;
+ struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.a);
+ GsuArray gsu = (GsuArray)(c->g);
+ if (locallevel >= pm->level)
+ return gsu->getfn(pm);
+ else
+ return &nullarray;
+}
+
+/**/
+static void
+ppa_setfn(Param pm, char **x)
+{
+ struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.a);
+ GsuArray gsu = (GsuArray)(c->g);
+ if (locallevel == pm->level)
+ gsu->setfn(pm, x);
+ else
+ setfn_error(pm);
+}
+
+/**/
+static void
+ppa_unsetfn(Param pm, int x)
+{
+ struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.a);
+ GsuArray gsu = (GsuArray)(c->g);
+ pm->gsu.a = gsu;
+ if (locallevel <= pm->level)
+ gsu->unsetfn(pm, x);
+}
+
+static HashTable emptytable;
+
+/**/
+static HashTable
+pph_getfn(Param pm)
+{
+ struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.h);
+ GsuHash gsu = (GsuHash)(c->g);
+ if (locallevel >= pm->level)
+ return gsu->getfn(pm);
+ else
+ return emptytable;
+}
+
+/**/
+static void
+pph_setfn(Param pm, HashTable x)
+{
+ struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.h);
+ GsuHash gsu = (GsuHash)(c->g);
+ if (locallevel == pm->level)
+ gsu->setfn(pm, x);
+ else
+ setfn_error(pm);
+}
+
+/**/
+static void
+pph_unsetfn(Param pm, int x)
+{
+ struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.h);
+ GsuHash gsu = (GsuHash)(c->g);
+ pm->gsu.h = gsu;
+ if (locallevel <= pm->level)
+ gsu->unsetfn(pm, x);
+}
+
+/*
+ * How visibility works:
+ *
+ * Upon entering a new function scope, we find all the private parameters
+ * at this locallevel. Any that we find are marked PM_UNSET. If they are
+ * already unset, they are also marked as PM_NORESTORE.
+ *
+ * On exit from the scope, we find the same parameters again and remove
+ * the PM_UNSET and PM_NORESTORE flags as appropriate. We're guaraneed
+ * by makeprivate() that PM_NORESTORE won't conflict with anything here.
+ *
+ */
+
+static void
+scopeprivate(HashNode hn, int onoff)
+{
+ Param pm = (Param)hn;
+ if (pm->level == locallevel) {
+ if (!is_private(pm))
+ return;
+ if (onoff == PM_UNSET)
+ if (pm->node.flags & PM_UNSET)
+ pm->node.flags |= PM_NORESTORE;
+ else
+ pm->node.flags |= PM_UNSET;
+ else if (!(pm->node.flags & PM_NORESTORE))
+ pm->node.flags &= ~PM_UNSET;
+ pm->node.flags &= ~PM_NORESTORE;
+ }
+}
+
+static struct funcwrap wrapper[] = {
+ WRAPDEF(wrap_private)
+};
+
+/**/
+static int
+wrap_private(Eprog prog, FuncWrap w, char *name)
+{
+ static int wraplevel = 0;
+
+ if (wraplevel < locallevel /* && strcmp(name, "(anon)") != 0 */) {
+ int owl = wraplevel;
+ wraplevel = locallevel;
+ scanhashtable(paramtab, 0, 0, 0, scopeprivate, PM_UNSET);
+ runshfunc(prog, w, name);
+ scanhashtable(paramtab, 0, 0, 0, scopeprivate, 0);
+ wraplevel = owl;
+ return 0;
+ }
+ return 1;
+}
+
+static HashNode (*getparamnode) _((HashTable, const char *));
+
+/**/
+static HashNode
+getprivatenode(HashTable ht, const char *nam)
+{
+ HashNode hn = getparamnode(ht, nam);
+ Param pm = (Param) hn;
+
+ while (!fakelevel && pm && locallevel > pm->level && is_private(pm))
+ pm = pm->old;
+ return (HashNode)pm;
+}
+
+/**/
+static HashNode
+getprivatenode2(HashTable ht, const char *nam)
+{
+ /* getparamnode() would follow autoloads, we must not do that here */
+ HashNode hn = gethashnode2(ht, nam);
+ Param pm = (Param) hn;
+
+ while (!fakelevel && pm && locallevel > pm->level && is_private(pm))
+ pm = pm->old;
+ return (HashNode)pm;
+}
+
+/**/
+static void
+printprivatenode(HashNode hn, int printflags)
+{
+ Param pm = (Param) hn;
+ while (pm && (!fakelevel ||
+ (fakelevel > pm->level && (pm->node.flags & PM_UNSET))) &&
+ locallevel > pm->level && is_private(pm))
+ pm = pm->old;
+ /* Ideally, we'd print the word "private" here instead of "typeset"
+ * when the parameter is in fact a private, but that would require
+ * re-implementing the entirety of printparamnode(). */
+ if (pm)
+ printparamnode((HashNode)pm, printflags);
+}
+
+/*
+ * Standard module configuration/linkage
+ */
+
+static struct builtin bintab[] = {
+ /* Copied from BUILTIN("local"), "P" added */
+ BUILTIN("private", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_private, 0, -1, 0, "AE:%F:%HL:%PR:%TUZ:%ahi:%lprtux", "P")
+};
+
+static struct features module_features = {
+ bintab, sizeof(bintab)/sizeof(*bintab),
+ NULL, 0,
+ NULL, 0,
+ NULL, 0,
+ 0
+};
+
+static struct builtin save_local;
+static struct reswd reswd_private = {{NULL, "private", 0}, TYPESET};
+
+/**/
+int
+setup_(UNUSED(Module m))
+{
+ HashNode hn = builtintab->getnode(builtintab, "local");
+
+ /* Horrible, horrible hack */
+ getparamnode = realparamtab->getnode;
+ realparamtab->getnode = getprivatenode;
+ realparamtab->getnode2 = getprivatenode2;
+ realparamtab->printnode = printprivatenode;
+
+ /* Even more horrible hack */
+ save_local = *(Builtin)hn;
+ ((Builtin)hn)->handlerfunc = bintab[0].handlerfunc;
+ ((Builtin)hn)->optstr = bintab[0].optstr;
+
+ reswdtab->addnode(reswdtab, reswd_private.node.nam, &reswd_private);
+
+ 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)
+{
+ emptytable = newparamtable(1, "private");
+ return addwrapper(m, wrapper);
+}
+
+/**/
+int
+cleanup_(Module m)
+{
+ HashNode hn = builtintab->getnode(builtintab, "local");
+ *(Builtin)hn = save_local;
+
+ removehashnode(reswdtab, "private");
+
+ realparamtab->getnode = getparamnode;
+ realparamtab->getnode2 = gethashnode2;
+ realparamtab->printnode = printparamnode;
+
+ deletewrapper(m, wrapper);
+ return setfeatureenables(m, &module_features, NULL);
+}
+
+/**/
+int
+finish_(UNUSED(Module m))
+{
+ deletehashtable(emptytable);
+ return 0;
+}