From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 21959 invoked from network); 6 Jul 1999 13:05:37 -0000 Received: from sunsite.auc.dk (130.225.51.30) by ns1.primenet.com.au with SMTP; 6 Jul 1999 13:05:37 -0000 Received: (qmail 25239 invoked by alias); 6 Jul 1999 13:05:12 -0000 Mailing-List: contact zsh-workers-help@sunsite.auc.dk; run by ezmlm Precedence: bulk X-No-Archive: yes X-Seq: 6986 Received: (qmail 25231 invoked from network); 6 Jul 1999 13:05:05 -0000 Message-Id: <9907061236.AA21182@ibmth.df.unipi.it> To: zsh-workers@sunsite.auc.dk (Zsh hackers list) Subject: PATCH: pws-25: local special parameters Date: Tue, 06 Jul 1999 14:36:24 +0200 From: Peter Stephenson This patch makes it possible to make special parameters local, while remaining special. What's more, it does it in a way which is supposed to be consistent and transparent. The road to hell is paved with good intentions. The main point to note is that only the global value can be in the environment --- strictly, it is possible for a local parameter to be in the environment with ALLEXPORT if there was no global parameter, but that can't happen with specials. This is entirely consistent with ksh, and with the current rule for normal parameters in zsh. I discovered a couple of glitches with the way this was working; for example, typeset -x did put the parameter in the environment, but as there was no code for restoring the original environment value it was lost at end of scope. The possible problems with dual specials like path/PATH are hair-raising. I think I've solved them in the most natural way, but it will need some testing. You should now be able to do things like: fn() { typeset PATH=/extra/bit:$PATH; # remember, external programmes here see global PATH ... } fn() { typeset IFS=:; read -a arr < colonfile; } fn() { # you'd better be root to begin with typeset USERNAME=juser ... } The only possible compatibility problem I can think of is if someone was using typeset inside a function to change some minor parameter attribute, relying on it not becoming local; this seems fairly unlikely. You will now need the -g flag to get the same effect. --- Doc/Zsh/builtins.yo.ns Sun Jul 4 17:25:25 1999 +++ Doc/Zsh/builtins.yo Tue Jul 6 13:49:13 1999 @@ -924,7 +924,8 @@ ifnzman(noderef(Local Parameters))\ . Local parameters are not exported unless tt(ALL_EXPORT) is set, in which case the parameter is exported em(only) when var(name) does not -already appear in the environment. +already exist. The same rules apply to special shell parameters, which +retain their special attributes when made local. For each var(name)tt(=)var(value) assignment, the parameter var(name) set to var(value). Note that arrays currently cannot be --- Doc/Zsh/params.yo.ns Mon Jun 28 19:01:59 1999 +++ Doc/Zsh/params.yo Tue Jul 6 14:01:41 1999 @@ -188,8 +188,7 @@ (Parameters are dynamically scoped.) The tt(typeset) builtin, and its alternative forms tt(declare), tt(integer), tt(local) and tt(readonly) (but not tt(export)), can be used to declare a parameter as being local -to the innermost scope. Note that em(special) parameters cannot be made -local. +to the innermost scope. When a parameter is read or assigned to, the innermost existing parameter of that name is used. (That is, the @@ -200,6 +199,23 @@ Local parameters disappear when their scope ends. tt(unset) can be used to delete a parameter while it is still in scope; any outer parameter of the same name remains hidden. + +Special parameters may also be made local; they retain their special +attributes. This may have unexpected effects. Firstly, there is no +default value, so if there is no assigment at the point the variable is +made local, it will be set to an empty value (or zero in the case of +integers). Secondly, special parameters which are made local will not be +exported (as with other parameters), so that the global value of the +parameter remains present in the environment if it is already there. This +should be particularly noted in the case of tt(PATH): the shell will use +the local version of tt(PATH) for finding programmes, but programmes using +the shell's environment will inherit the global version. The following: + +example(typeset PATH=/new/directory:$PATH) + +is valid for temporarily allowing the shell to find the programs in +tt(/new/directory) inside a function. + texinode(Parameters Set By The Shell)(Parameters Used By The Shell)(Local Parameters)(Parameters) sect(Parameters Set By The Shell) The following parameters are automatically set by the shell: --- Src/builtin.c.ns Sun Jul 4 17:24:14 1999 +++ Src/builtin.c Tue Jul 6 14:29:18 1999 @@ -645,12 +645,14 @@ setsparam("OLDPWD", ztrdup(oldpwd)); pm = (Param) paramtab->getnode(paramtab, "PWD"); - if (!(pm->flags & PM_EXPORTED)) { + if (!(pm->flags & PM_EXPORTED) && + (!pm->level || (isset(ALLEXPORT) && !pm->old))) { pm->flags |= PM_EXPORTED; pm->env = addenv("PWD", pwd); } pm = (Param) paramtab->getnode(paramtab, "OLDPWD"); - if (!(pm->flags & PM_EXPORTED)) { + if (!(pm->flags & PM_EXPORTED) && + (!pm->level || (isset(ALLEXPORT) && !pm->old))) { pm->flags |= PM_EXPORTED; pm->env = addenv("OLDPWD", oldpwd); } @@ -1492,7 +1494,7 @@ typeset_single(char *cname, char *pname, Param pm, int func, int on, int off, int roff, char *value, Param altpm) { - int usepm, tc, keeplocal = 0; + int usepm, tc, keeplocal = 0, newspecial = 0; /* * Do we use the existing pm? Note that this isn't the end of the @@ -1503,22 +1505,31 @@ */ usepm = pm && !(pm->flags & PM_UNSET); - /* Always use an existing pm if special at current locallevel */ - if (pm && (pm->flags & PM_SPECIAL) && pm->level == locallevel) + /* + * We need to compare types with an existing pm if special, + * even if that's unset + */ + if (pm && (pm->flags & PM_SPECIAL)) usepm = 1; /* - * Don't use a non-special existing param if + * Don't use an existing param if * - the local level has changed, and * - we are really locallizing the parameter */ - if (usepm && !(pm->flags & PM_SPECIAL) && - locallevel != pm->level && (on & PM_LOCAL)) + if (usepm && locallevel != pm->level && (on & PM_LOCAL)) { + /* + * If the original parameter was special and we're creating + * a new one, we need to keep it special. + */ + newspecial = (pm->flags & PM_SPECIAL); usepm = 0; + } /* attempting a type conversion, or making a tied colonarray? */ - if ((tc = usepm && (((off & pm->flags) | (on & ~pm->flags)) & - (PM_INTEGER|PM_HASHED|PM_ARRAY|PM_TIED|PM_AUTOLOAD)))) + if ((tc = (usepm || newspecial) + && (((off & pm->flags) | (on & ~pm->flags)) & + (PM_INTEGER|PM_HASHED|PM_ARRAY|PM_TIED|PM_AUTOLOAD)))) usepm = 0; if (tc && (pm->flags & PM_SPECIAL)) { zerrnam(cname, "%s: can't change type of a special parameter", @@ -1526,13 +1537,27 @@ return NULL; } + /* + * According to the manual, local parameters don't get exported. + * A parameter will be local if + * 1. we are re-using an existing local parameter + * or + * 2. we are not using an existing parameter, but + * i. there is already a parameter, which will be hidden + * or + * ii. we are creating a new local parameter + */ + if ((usepm && pm->level) || + (!usepm && (pm || (locallevel && (on & PM_LOCAL))))) + on &= ~PM_EXPORTED; + if (usepm) { on &= ~PM_LOCAL; if (!on && !roff && !value) { paramtab->printnode((HashNode)pm, 0); return pm; } - if ((pm->flags & PM_RESTRICTED && isset(RESTRICTED))) { + if ((pm->flags & PM_RESTRICTED) && isset(RESTRICTED)) { zerrnam(cname, "%s: restricted", pname, 0); return pm; } @@ -1553,7 +1578,8 @@ if (pm->flags & PM_EXPORTED) { if (!(pm->flags & PM_UNSET) && !pm->env && !value) pm->env = addenv(pname, getsparam(pname)); - } else if (pm->env) { + } else if (pm->env && + (!pm->level || (isset(ALLEXPORT) && !pm->old))) { delenv(pm->env); zsfree(pm->env); pm->env = NULL; @@ -1593,13 +1619,68 @@ pname = dupstring(pname); unsetparam_pm(pm, 0, 1); } - /* - * Create a new node for a parameter with the flags in `on' minus the - * readonly flag - */ - pm = createparam(pname, on & ~PM_READONLY); - DPUTS(!pm, "BUG: parameter not created"); - pm->ct = auxlen; + + if (newspecial) { + Param tpm, pm2; + if ((pm->flags & PM_RESTRICTED) && isset(RESTRICTED)) { + zerrnam(cname, "%s: restricted", pname, 0); + return pm; + } + /* + * For specials, we keep the same struct but zero everything. + * Maybe it would be easier to create a new struct but copy + * the get/set methods. + */ + tpm = (Param) zalloc(sizeof *tpm); + + tpm->nam = pm->nam; + if (pm->ename && + (pm2 = (Param) paramtab->getnode(paramtab, pm->ename)) && + pm2->level == locallevel) { + /* This is getting silly, but anyway: if one of a path/PATH + * pair has already been made local at the current level, we + * have to make sure that the other one does not have its value + * saved: since that comes from an internal variable it will + * already reflect the local value, so restoring it on exit + * would be wrong. + * + * This problem is also why we make sure we have a copy + * of the environment entry in tpm->env, rather than relying + * on the restored value to provide it. + */ + tpm->flags = pm->flags | PM_NORESTORE; + } else { + copyparam(tpm, pm, 1); + } + tpm->old = pm->old; + tpm->level = pm->level; + tpm->ct = pm->ct; + tpm->env = pm->env; + + pm->old = tpm; + /* + * The remaining on/off flags should be harmless to use, + * because we've checked for unpleasant surprises above. + */ + pm->flags = (PM_TYPE(pm->flags) | on | PM_SPECIAL) & ~off; + /* + * Final tweak: if we've turned on one of the flags with + * numbers, we should use the appropriate integer. + */ + if (on & (PM_LEFT|PM_RIGHT_B|PM_RIGHT_Z|PM_INTEGER)) + pm->ct = auxlen; + else + pm->ct = 0; + pm->env = NULL; + } else { + /* + * Create a new node for a parameter with the flags in `on' minus the + * readonly flag + */ + pm = createparam(pname, on & ~PM_READONLY); + DPUTS(!pm, "BUG: parameter not created"); + pm->ct = auxlen; + } if (altpm && PM_TYPE(pm->flags) == PM_SCALAR) { /* @@ -1618,6 +1699,28 @@ pm->level = locallevel; if (value && !(pm->flags & (PM_ARRAY|PM_HASHED))) setsparam(pname, ztrdup(value)); + else if (newspecial && !(pm->old->flags & PM_NORESTORE)) { + /* + * We need to use the special setting function to re-initialise + * the special parameter to empty. + */ + HEAPALLOC { + switch (PM_TYPE(pm->flags)) { + case PM_SCALAR: + pm->sets.cfn(pm, ztrdup("")); + break; + case PM_INTEGER: + pm->sets.ifn(pm, 0); + break; + case PM_ARRAY: + pm->sets.afn(pm, mkarray(NULL)); + break; + case PM_HASHED: + pm->sets.hfn(pm, newparamtable(17, pm->nam)); + break; + } + } LASTALLOC; + } pm->flags |= (on & PM_READONLY); if (value && (pm->flags & (PM_ARRAY|PM_HASHED))) { zerrnam(cname, "%s: can't assign initial value for array", pname, 0); --- Src/params.c.ns Fri Jun 25 13:45:11 1999 +++ Src/params.c Tue Jul 6 13:31:11 1999 @@ -2500,11 +2500,17 @@ Param pm; MUSTUSEHEAP("arrfixenv"); + pm = (Param) paramtab->getnode(paramtab, s); + /* + * Only one level of a parameter can be exported. Unless + * ALLEXPORT is set, this must be global. + */ if (t == path) cmdnamtab->emptytable(cmdnamtab); + if (isset(ALLEXPORT) ? !!pm->old : pm->level) + return; u = t ? zjoin(t, ':') : ""; len_s = strlen(s); - pm = (Param) paramtab->getnode(paramtab, s); for (ep = environ; *ep; ep++) if (!strncmp(*ep, s, len_s) && (*ep)[len_s] == '=') { pm->env = replenv(*ep, u); @@ -2685,8 +2691,46 @@ scanendscope(HashNode hn, int flags) { Param pm = (Param)hn; - if(pm->level > locallevel) - unsetparam_pm(pm, 0, 0); + if (pm->level > locallevel) { + if ((pm->flags & (PM_SPECIAL|PM_REMOVABLE)) == PM_SPECIAL) { + /* + * Removable specials are normal in that they can be removed + * to reveal an ordinary parameter beneath. Here we handle + * non-removable specials, which were made local by stealth + * (see newspecial code in typeset_single()). In fact the + * visible pm is always the same struct; the pm->old is + * just a place holder for old data and flags. + */ + Param tpm = pm->old; + + DPUTS(!tpm || PM_TYPE(pm->flags) != PM_TYPE(tpm->flags) || + !(tpm->flags & PM_SPECIAL), + "BUG: in restoring scope of special parameter"); + pm->old = tpm->old; + pm->flags = (tpm->flags & ~PM_NORESTORE); + pm->level = tpm->level; + pm->ct = tpm->ct; + pm->env = tpm->env; + + if (!(tpm->flags & PM_NORESTORE)) + switch (PM_TYPE(pm->flags)) { + case PM_SCALAR: + pm->sets.cfn(pm, tpm->u.str); + break; + case PM_INTEGER: + pm->sets.ifn(pm, tpm->u.val); + break; + case PM_ARRAY: + pm->sets.afn(pm, tpm->u.arr); + break; + case PM_HASHED: + pm->sets.hfn(pm, tpm->u.hash); + break; + } + zfree(tpm, sizeof(*tpm)); + } else + unsetparam_pm(pm, 0, 0); + } } --- Src/zsh.h.ns Tue Jul 6 09:25:44 1999 +++ Src/zsh.h Tue Jul 6 14:08:46 1999 @@ -951,40 +951,41 @@ /* flags for parameters */ /* parameter types */ -#define PM_SCALAR 0 /* scalar */ -#define PM_ARRAY (1<<0) /* array */ -#define PM_INTEGER (1<<1) /* integer */ -#define PM_HASHED (1<<2) /* association */ +#define PM_SCALAR 0 /* scalar */ +#define PM_ARRAY (1<<0) /* array */ +#define PM_INTEGER (1<<1) /* integer */ +#define PM_HASHED (1<<2) /* association */ #define PM_TYPE(X) (X & (PM_SCALAR|PM_INTEGER|PM_ARRAY|PM_HASHED)) -#define PM_LEFT (1<<3) /* left justify and remove leading blanks */ -#define PM_RIGHT_B (1<<4) /* right justify and fill with leading blanks */ -#define PM_RIGHT_Z (1<<5) /* right justify and fill with leading zeros */ -#define PM_LOWER (1<<6) /* all lower case */ +#define PM_LEFT (1<<3) /* left justify, remove leading blanks */ +#define PM_RIGHT_B (1<<4) /* right justify, fill with leading blanks */ +#define PM_RIGHT_Z (1<<5) /* right justify, fill with leading zeros */ +#define PM_LOWER (1<<6) /* all lower case */ /* The following are the same since they * * both represent -u option to typeset */ -#define PM_UPPER (1<<7) /* all upper case */ -#define PM_UNDEFINED (1<<7) /* undefined (autoloaded) shell function */ +#define PM_UPPER (1<<7) /* all upper case */ +#define PM_UNDEFINED (1<<7) /* undefined (autoloaded) shell function */ -#define PM_READONLY (1<<8) /* readonly */ -#define PM_TAGGED (1<<9) /* tagged */ -#define PM_EXPORTED (1<<10) /* exported */ +#define PM_READONLY (1<<8) /* readonly */ +#define PM_TAGGED (1<<9) /* tagged */ +#define PM_EXPORTED (1<<10) /* exported */ /* The following are the same since they * * both represent -U option to typeset */ -#define PM_UNIQUE (1<<11) /* remove duplicates */ -#define PM_UNALIASED (1<<11) /* do not expand aliases when autoloading */ +#define PM_UNIQUE (1<<11) /* remove duplicates */ +#define PM_UNALIASED (1<<11) /* do not expand aliases when autoloading */ -#define PM_TIED (1<<12) /* array tied to colon-path or v.v. */ -#define PM_LOCAL (1<<13) /* this parameter will be made local */ -#define PM_SPECIAL (1<<14) /* special builtin parameter */ -#define PM_DONTIMPORT (1<<15) /* do not import this variable */ -#define PM_RESTRICTED (1<<16) /* cannot be changed in restricted mode */ -#define PM_UNSET (1<<17) /* has null value */ -#define PM_REMOVABLE (1<<18) /* special can be removed from paramtab */ -#define PM_AUTOLOAD (1<<19) /* autoloaded from module */ +#define PM_TIED (1<<12) /* array tied to colon-path or v.v. */ +#define PM_LOCAL (1<<13) /* this parameter will be made local */ +#define PM_SPECIAL (1<<14) /* special builtin parameter */ +#define PM_DONTIMPORT (1<<15) /* do not import this variable */ +#define PM_RESTRICTED (1<<16) /* cannot be changed in restricted mode */ +#define PM_UNSET (1<<17) /* has null value */ +#define PM_REMOVABLE (1<<18) /* special can be removed from paramtab */ +#define PM_AUTOLOAD (1<<19) /* autoloaded from module */ +#define PM_NORESTORE (1<<20) /* do not restore value of local special */ /* Flags for extracting elements of arrays and associative arrays */ #define SCANPM_WANTVALS (1<<0) -- Peter Stephenson Tel: +39 050 844536 WWW: http://www.ifh.de/~pws/ Dipartimento di Fisica, Via Buonarroti 2, 56127 Pisa, Italy