From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: zsh-workers-return-43616-ml=inbox.vuxu.org@zsh.org X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on inbox.vuxu.org X-Spam-Level: X-Spam-Status: No, score=-1.1 required=5.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,FREEMAIL_FROM,MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE autolearn=ham autolearn_force=no version=3.4.2 Received: from primenet.com.au (ns1.primenet.com.au [203.24.36.2]) by inbox.vuxu.org (OpenSMTPD) with ESMTP id 653b72c0 for ; Sun, 7 Oct 2018 13:36:16 +0000 (UTC) Received: (qmail 424 invoked by alias); 7 Oct 2018 13:35:57 -0000 Mailing-List: contact zsh-workers-help@zsh.org; run by ezmlm Precedence: bulk X-No-Archive: yes List-Id: Zsh Workers List List-Post: List-Help: List-Unsubscribe: X-Seq: 43616 Received: (qmail 17275 invoked by uid 1010); 7 Oct 2018 13:35:57 -0000 X-Qmail-Scanner-Diagnostics: from mail-wr1-f43.google.com by f.primenet.com.au (envelope-from , uid 7791) with qmail-scanner-2.11 (clamdscan: 0.99.2/21882. spamassassin: 3.4.1. Clear:RC:0(209.85.221.43):SA:0(-1.9/5.0):. Processed in 3.44642 secs); 07 Oct 2018 13:35:57 -0000 X-Envelope-From: stephane.chazelas@gmail.com X-Qmail-Scanner-Mime-Attachments: | X-Qmail-Scanner-Zip-Files: | DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=date:from:to:subject:message-id:mail-followup-to:references :mime-version:content-disposition:in-reply-to:user-agent; bh=r4YsGpbfODnMefTByr7KFvRHT4HQ5NCmxA670YXj4zY=; b=erippoETSS/COVr2LgDRqe8kebMSpAeNYuKSeeFCJK1y9+qPUBeEkRZ/YGxM2jg+0e eObgL9QiASgvopzpKbvkHlBTkkYlq0Lkfhsk2kOkX/sUTb4y8GDnopSF68xHdzJY5NVP iTZjG9o/w4ptODuyG2++HpCEGKJ/V1s1qJoTsJFI20h96nnsc9IMPNlePBdUEt47ASiu zxFL7lSc3Utq+qCH5h9l02YwZ/gf1YJkfmOXsmbkJ9uNK7WdVuV6uk+J0OO7KkxAEfIC 8JZsQcYk2w6Fz65JReCnLpoQCWCteIiG0wy49G2vijZoqDgzFY1wnQl+DFiv+HTds7kS aBGA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:subject:message-id:mail-followup-to :references:mime-version:content-disposition:in-reply-to:user-agent; bh=r4YsGpbfODnMefTByr7KFvRHT4HQ5NCmxA670YXj4zY=; b=WxYZmohzfC8c+7+QU0ftWhoG8JUqVBwXRizfcB2e73yZ0RAEGkF61XIFCA7Bc9QYEW A24xvRuwCu1ZhRTHg+xi0L7JXA5RNeRbiXmPqBLH+O+iDayQwoZEuU3t0oxmFpm1qQHu GjinGyToZOffz17iuvAM4z5FDYKhNlBqoVW1F/ignvHZaL/IMCpPghX71sClUpAAtNFF P00h659R1iOwlGuCiNctOPjUr3fDkhC5TdYrKByQWTFfGMkM4z/dOpvbm3JvcBtMFyRp 0s0/+rkxxHW//kOPcWdzdIhu2rGVjl+I0o9ixpRLTdF9ZImlm5Xk+SYXC4VLR/KKJgrw gPZA== X-Gm-Message-State: ABuFfoir1b4HOWGFyswSyTKeRFSDl+FKCIcrU3P/XYpysHdh3ZCqtYPk WGT2LZMtGZp2qUANM+lX6GcnJLQ97kE= X-Google-Smtp-Source: ACcGV63XXTU/NX/Ir5Idx9C5VSHxBmxGRuQ+HSuHqiIyjAc9KXpwQU13p9+GagEteXlRQIkEnuXGKg== X-Received: by 2002:a5d:6450:: with SMTP id d16-v6mr12475348wrw.64.1538919348478; Sun, 07 Oct 2018 06:35:48 -0700 (PDT) Date: Sun, 7 Oct 2018 14:35:46 +0100 From: Stephane Chazelas To: Zsh hackers list Subject: [PATCH] [long] typeset doesn't report tied parameters (and related issues) Message-ID: <20181007133545.zzkrbc3ed6shnk3e@chaz.gmail.com> Mail-Followup-To: Zsh hackers list References: <20180924210550.carijwjibarjivu4@chaz.gmail.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20180924210550.carijwjibarjivu4@chaz.gmail.com> User-Agent: NeoMutt/20171215 2018-09-24 22:05:50 +0100, Stephane Chazelas: > From: > https://unix.stackexchange.com/questions/470956/how-to-print-all-the-attributes-of-a-zsh-parameter [...] Hi, that's a follow up on my previous patch about typeset -p to report unique parameters. It would be useful for "typeset -p", or "typeset +m" or ${(t)param} or $parameters[param] to report the fact that the array is tied (and how for typeset -p) including for special ones that are created as such (like PATH to path). See also discussion at https://unix.stackexchange.com/questions/470956/how-to-print-all-the-attributes-of-a-zsh-parameter#comment860654_471031 This patch attempts to address that. While writing it, I found a number of related "issues" or possible areas of improvement, most of them minor. That ends up being quite a large change. I didn't initially intend to spend that much effort on it, I'm not very familiar with the source code (though a lot more now than when I started). So even though I've done quite a bit of testing, I think it should be reviewed by someone with more intimate knowledge of the code. Other issues: - typeset -T A a; echo ${(t)a} scalar-tag_local "tag_local" is a "function" thing. Changed to "-tied" - $ typeset -T A=a:b a +; typeset -T A a; echo $a a:b Change: force an assignment upon change to join character (I suspect there's a better way to do that one) - typeset +x -T VAR var doesn't honour the +x - $ zsh -c 'manpath=(a:b c); typeset -p manpath; (){local MANPATH}; typeset -p manpath' typeset -a manpath=( a:b c ) typeset -a manpath=( a b c ) Not addressed by this patch. More generally, in those cases (same for tied=() vs tied=('') which cannot be preserved upon conversion to colonarray) it's not always clear when and how it's going to be "fixed", but I think we can live with it. - zsh 'readonly PATH; readonly -p' prints nothing. That's because readonly specials are not output. But I think the intention was for specials that are read-only because they are so by design as they are only be meant to be set by the shell (things like $#, $?, $PID...). However for the ones that the user set as read-only, they should be output by "readonly". It's quite common to make specials readonly (like IFS, PATH...) - attributes of read-only params can be changed even in POSIX mode like in ksh. Not changed, but documented. - $ ARGV0=sh zsh -c 'readonly A=1; readonly -p' typeset -r A=1 Changed to "readonly A=1" when POSIXBUILTIN is on and not in ksh emulation. That one may not be necessary. The idea is to be able to pass the output of "export -p" or "readonly -p" to other POSIX shells. But then note: $ emulate sh $ a=$'\x80' export -p a export a=$'\M-\C-@' POSIX is currently specifying $'...', currently including \c@, \x80, \200, but not \C-@ nor \M-X. IMO, these days, outputting \x80 here would be more useful. http://austingroupbugs.net/view.php?id=249#c590 still work in progress. diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo index 0141305b4..4039595df 100644 --- a/Doc/Zsh/builtins.yo +++ b/Doc/Zsh/builtins.yo @@ -2029,6 +2029,9 @@ scalar version causes a split on all separators (which cannot be quoted). It is possible to apply tt(-T) to two previously tied variables but with a different separator character, in which case the variables remain joined as before but the separator is changed. + +When an existing scalar is tied to a new array, the value of the scalar +is preserved but no attribute other than export will be preserved. ) enditem() @@ -2076,12 +2079,12 @@ flag. ) item(tt(-U))( For arrays (but not for associative arrays), keep only the first -occurrence of each duplicated value. This may also be set for -colon-separated special parameters like tt(PATH) or tt(FIGNORE), etc. -Note the flag takes effect on assignment, and the type of the -variable being assigned to is determinative; for variables with -shared values it is therefore recommended to set the flag for -all interfaces, e.g. `tt(typeset -U PATH path)'. +occurrence of each duplicated value. This may also be set for tied +parameters (see tt(-T)) or colon-separated special parameters like +tt(PATH) or tt(FIGNORE), etc. Note the flag takes effect on assignment, +and the type of the variable being assigned to is determinative; for +variables with shared values it is therefore recommended to set the flag +for all interfaces, e.g. `tt(typeset -U PATH path)'. This flag has a different meaning when used with tt(-f); see below. ) @@ -2174,10 +2177,17 @@ be turned off. If the tt(POSIX_BUILTINS) option is set, the readonly attribute is more restrictive: unset variables can be marked readonly and cannot then be set; furthermore, the readonly attribute cannot be removed from any -variable. Note that in zsh (unlike other shells) it is still possible -to create a local variable of the same name as this is considered a -different variable (though this variable, too, can be marked readonly). -) +variable. + +It is still possible to change other attributes of the variable though, +some of which like tt(-U) or tt(-Z) would affect the value. More generally, +the readonly attribute should not be relied on as a security mechanism. + +Note that in zsh (like in pdksh but unlike most other shells) it is +still possible to create a local variable of the same name as this is +considered a different variable (though this variable, too, can be marked +readonly). Special variables that have been made readonly retain their value +and readonly attribute when made local.) item(tt(-t))( Tags the named parameters. Tags have no special meaning to the shell. This flag has a different meaning when used with tt(-f); see above. diff --git a/Src/Modules/db_gdbm.c b/Src/Modules/db_gdbm.c index ed702b912..12dd839cf 100644 --- a/Src/Modules/db_gdbm.c +++ b/Src/Modules/db_gdbm.c @@ -809,7 +809,7 @@ myfreeparamnode(HashNode hn) zsfree(pm->node.nam); /* If this variable was tied by the user, ename was ztrdup'd */ - if (pm->node.flags & PM_TIED && pm->ename) { + if (!(pm->node.flags & PM_SPECIAL) && pm->ename) { zsfree(pm->ename); pm->ename = NULL; } diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c index 783c36df3..76824cf58 100644 --- a/Src/Modules/parameter.c +++ b/Src/Modules/parameter.c @@ -75,6 +75,8 @@ paramtypestr(Param pm) val = dyncat(val, "-readonly"); if (f & PM_TAGGED) val = dyncat(val, "-tag"); + if (f & PM_TIED) + val = dyncat(val, "-tied"); if (f & PM_EXPORTED) val = dyncat(val, "-export"); if (f & PM_UNIQUE) @@ -2194,67 +2196,67 @@ static const struct gsu_array historywords_gsu = static struct paramdef partab[] = { SPECIALPMDEF("aliases", 0, &pmraliases_gsu, getpmralias, scanpmraliases), - SPECIALPMDEF("builtins", PM_READONLY, NULL, getpmbuiltin, scanpmbuiltins), + SPECIALPMDEF("builtins", PM_READONLY_SPECIAL, NULL, getpmbuiltin, scanpmbuiltins), SPECIALPMDEF("commands", 0, &pmcommands_gsu, getpmcommand, scanpmcommands), SPECIALPMDEF("dirstack", PM_ARRAY, &dirs_gsu, NULL, NULL), SPECIALPMDEF("dis_aliases", 0, &pmdisraliases_gsu, getpmdisralias, scanpmdisraliases), - SPECIALPMDEF("dis_builtins", PM_READONLY, + SPECIALPMDEF("dis_builtins", PM_READONLY_SPECIAL, NULL, getpmdisbuiltin, scanpmdisbuiltins), SPECIALPMDEF("dis_functions", 0, &pmdisfunctions_gsu, getpmdisfunction, scanpmdisfunctions), - SPECIALPMDEF("dis_functions_source", PM_READONLY, NULL, + SPECIALPMDEF("dis_functions_source", PM_READONLY_SPECIAL, NULL, getpmdisfunction_source, scanpmdisfunction_source), SPECIALPMDEF("dis_galiases", 0, &pmdisgaliases_gsu, getpmdisgalias, scanpmdisgaliases), - SPECIALPMDEF("dis_patchars", PM_ARRAY|PM_READONLY, + SPECIALPMDEF("dis_patchars", PM_ARRAY|PM_READONLY_SPECIAL, &dispatchars_gsu, NULL, NULL), - SPECIALPMDEF("dis_reswords", PM_ARRAY|PM_READONLY, + SPECIALPMDEF("dis_reswords", PM_ARRAY|PM_READONLY_SPECIAL, &disreswords_gsu, NULL, NULL), SPECIALPMDEF("dis_saliases", 0, &pmdissaliases_gsu, getpmdissalias, scanpmdissaliases), - SPECIALPMDEF("funcfiletrace", PM_ARRAY|PM_READONLY, + SPECIALPMDEF("funcfiletrace", PM_ARRAY|PM_READONLY_SPECIAL, &funcfiletrace_gsu, NULL, NULL), - SPECIALPMDEF("funcsourcetrace", PM_ARRAY|PM_READONLY, + SPECIALPMDEF("funcsourcetrace", PM_ARRAY|PM_READONLY_SPECIAL, &funcsourcetrace_gsu, NULL, NULL), - SPECIALPMDEF("funcstack", PM_ARRAY|PM_READONLY, + SPECIALPMDEF("funcstack", PM_ARRAY|PM_READONLY_SPECIAL, &funcstack_gsu, NULL, NULL), SPECIALPMDEF("functions", 0, &pmfunctions_gsu, getpmfunction, scanpmfunctions), - SPECIALPMDEF("functions_source", PM_READONLY, NULL, + SPECIALPMDEF("functions_source", PM_READONLY_SPECIAL, NULL, getpmfunction_source, scanpmfunction_source), - SPECIALPMDEF("functrace", PM_ARRAY|PM_READONLY, + SPECIALPMDEF("functrace", PM_ARRAY|PM_READONLY_SPECIAL, &functrace_gsu, NULL, NULL), SPECIALPMDEF("galiases", 0, &pmgaliases_gsu, getpmgalias, scanpmgaliases), - SPECIALPMDEF("history", PM_READONLY, + SPECIALPMDEF("history", PM_READONLY_SPECIAL, NULL, getpmhistory, scanpmhistory), - SPECIALPMDEF("historywords", PM_ARRAY|PM_READONLY, + SPECIALPMDEF("historywords", PM_ARRAY|PM_READONLY_SPECIAL, &historywords_gsu, NULL, NULL), - SPECIALPMDEF("jobdirs", PM_READONLY, + SPECIALPMDEF("jobdirs", PM_READONLY_SPECIAL, NULL, getpmjobdir, scanpmjobdirs), - SPECIALPMDEF("jobstates", PM_READONLY, + SPECIALPMDEF("jobstates", PM_READONLY_SPECIAL, NULL, getpmjobstate, scanpmjobstates), - SPECIALPMDEF("jobtexts", PM_READONLY, + SPECIALPMDEF("jobtexts", PM_READONLY_SPECIAL, NULL, getpmjobtext, scanpmjobtexts), - SPECIALPMDEF("modules", PM_READONLY, + SPECIALPMDEF("modules", PM_READONLY_SPECIAL, NULL, getpmmodule, scanpmmodules), SPECIALPMDEF("nameddirs", 0, &pmnameddirs_gsu, getpmnameddir, scanpmnameddirs), SPECIALPMDEF("options", 0, &pmoptions_gsu, getpmoption, scanpmoptions), - SPECIALPMDEF("parameters", PM_READONLY, + SPECIALPMDEF("parameters", PM_READONLY_SPECIAL, NULL, getpmparameter, scanpmparameters), - SPECIALPMDEF("patchars", PM_ARRAY|PM_READONLY, + SPECIALPMDEF("patchars", PM_ARRAY|PM_READONLY_SPECIAL, &patchars_gsu, NULL, NULL), - SPECIALPMDEF("reswords", PM_ARRAY|PM_READONLY, + SPECIALPMDEF("reswords", PM_ARRAY|PM_READONLY_SPECIAL, &reswords_gsu, NULL, NULL), SPECIALPMDEF("saliases", 0, &pmsaliases_gsu, getpmsalias, scanpmsaliases), - SPECIALPMDEF("userdirs", PM_READONLY, + SPECIALPMDEF("userdirs", PM_READONLY_SPECIAL, NULL, getpmuserdir, scanpmuserdirs), - SPECIALPMDEF("usergroups", PM_READONLY, + SPECIALPMDEF("usergroups", PM_READONLY_SPECIAL, NULL, getpmusergroups, scanpmusergroups) }; diff --git a/Src/builtin.c b/Src/builtin.c index 4abc7da35..c5b319b68 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -64,7 +64,7 @@ static struct builtin builtins[] = BUILTIN("enable", 0, bin_enable, 0, -1, BIN_ENABLE, "afmprs", NULL), BUILTIN("eval", BINF_PSPECIAL, bin_eval, 0, -1, BIN_EVAL, NULL, NULL), BUILTIN("exit", BINF_PSPECIAL, bin_break, 0, 1, BIN_EXIT, NULL, NULL), - BUILTIN("export", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "E:%F:%HL:%R:%TUZ:%afhi:%lp:%rtu", "xg"), + BUILTIN("export", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, BIN_EXPORT, "E:%F:%HL:%R:%TUZ:%afhi:%lp:%rtu", "xg"), BUILTIN("false", 0, bin_false, 0, -1, 0, NULL, NULL), /* * We used to behave as if the argument to -e was optional. @@ -2258,6 +2258,22 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), } else if (pm->env && !(pm->node.flags & PM_HASHELEM)) delenv(pm); DPUTS(ASG_ARRAYP(asg), "BUG: typeset got array value where scalar expected"); + if (altpm) { + struct tieddata* tdp = (struct tieddata *) pm->u.data; + if (tdp) { + if (tdp->joinchar != joinchar && !asg->value.scalar) { + /* + * Reassign the scalar to itself to do the splitting with + * the new joinchar + */ + tdp->joinchar = joinchar; + if (!(pm = assignsparam(pname, ztrdup(getsparam(pname)), 0))) + return NULL; + } + } + else + DPUTS(!tdp, "BUG: no join character to update"); + } if (asg->value.scalar && !(pm = assignsparam(pname, ztrdup(asg->value.scalar), 0))) return NULL; @@ -2325,6 +2341,9 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), zerrnam(cname, "%s: can only have a single instance", pname); return pm; } + + on |= pm->node.flags & PM_TIED; + /* * For specials, we keep the same struct but zero everything. * Maybe it would be easier to create a new struct but copy @@ -2476,7 +2495,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), return NULL; } - if (altpm && PM_TYPE(pm->node.flags) == PM_SCALAR) { + if (altpm && PM_TYPE(pm->node.flags) == PM_SCALAR && !(pm->node.flags & PM_SPECIAL)) { /* * It seems safer to set this here than in createparam(), * to make sure we only ever use the colonarr functions @@ -2646,7 +2665,17 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) /* Given no arguments, list whatever the options specify. */ if (OPT_ISSET(ops,'p')) { - printflags |= PRINT_TYPESET; + + if (isset(POSIXBUILTINS) && SHELL_EMULATION() != EMULATE_KSH) { + if (func == BIN_EXPORT) + printflags |= PRINT_POSIX_EXPORT; + else if (func == BIN_READONLY) + printflags |= PRINT_POSIX_READONLY; + else + printflags |= PRINT_TYPESET; + } else + printflags |= PRINT_TYPESET; + if (OPT_HASARG(ops,'p')) { char *eptr; int pflag = (int)zstrtol(OPT_ARG(ops,'p'), &eptr, 10); @@ -2662,13 +2691,20 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) } hasargs = *argv != NULL || (assigns && firstnode(assigns)); if (!hasargs) { + int exclude = 0; if (!OPT_ISSET(ops,'p')) { if (!(on|roff)) printflags |= PRINT_TYPE; if (roff || OPT_ISSET(ops,'+')) printflags |= PRINT_NAMEONLY; + } else if (printflags & (PRINT_POSIX_EXPORT|PRINT_POSIX_READONLY)) { + /* + * For POSIX export/readonly, exclude non-scalars unless + * explicitly requested. + */ + exclude = (PM_ARRAY|PM_HASHED) & ~(on|roff); } - scanhashtable(paramtab, 1, on|roff, 0, paramtab->printnode, printflags); + scanhashtable(paramtab, 1, on|roff, exclude, paramtab->printnode, printflags); unqueue_signals(); return 0; } @@ -2683,6 +2719,7 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) struct asgment asg0, asg2; char *oldval = NULL, *joinstr; int joinchar, nargs; + int already_tied = 0; if (OPT_ISSET(ops,'m')) { zwarnnam(name, "incompatible options for -T"); @@ -2765,47 +2802,81 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) joinchar = joinstr[1] ^ 32; else joinchar = *joinstr; - /* - * Keep the old value of the scalar. We need to do this - * here as if it is already tied to the same array it - * will be unset when we retie the array. This is all - * so that typeset -T is idempotent. - * - * We also need to remember here whether the damn thing is - * exported and pass that along. Isn't the world complicated? - */ - if ((pm = (Param) paramtab->getnode(paramtab, asg0.name)) - && !(pm->node.flags & PM_UNSET) - && (locallevel == pm->level || !(on & PM_LOCAL))) { - if (pm->node.flags & PM_TIED) { + + pm = (Param) paramtab->getnode(paramtab, asg0.name); + apm = (Param) paramtab->getnode(paramtab, asg->name); + + if (pm && (pm->node.flags & (PM_SPECIAL|PM_TIED)) == (PM_SPECIAL|PM_TIED)) { + /* + * Only allow typeset -T on special tied parameters if the tied + * parameter and join char are the same + */ + if (strcmp(pm->ename, asg->name) || !(apm->node.flags & PM_SPECIAL)) { + zwarnnam(name, "%s special parameter can only be tied to special parameter %s", asg0.name, pm->ename); + unqueue_signals(); + return 1; + } + if (joinchar != ':') { + zwarnnam(name, "cannot change the join character of special tied parameters"); unqueue_signals(); - if (PM_TYPE(pm->node.flags) != PM_SCALAR) { - zwarnnam(name, "already tied as non-scalar: %s", asg0.name); - } else if (!strcmp(asg->name, pm->ename)) { + return 1; + } + already_tied = 1; + } else if (apm && (apm->node.flags & (PM_SPECIAL|PM_TIED)) == (PM_SPECIAL|PM_TIED)) { + /* + * For the array variable, this covers attempts to tie the + * array to a different scalar or to the scalar after it has + * been made non-special + */ + zwarnnam(name, "%s special parameter can only be tied to special parameter %s", asg->name, apm->ename); + unqueue_signals(); + return 1; + } else if (pm) { + if (!(pm->node.flags & PM_UNSET) + && (locallevel == pm->level || !(on & PM_LOCAL))) { + if (pm->node.flags & PM_TIED) { + if (PM_TYPE(pm->node.flags) != PM_SCALAR) { + zwarnnam(name, "already tied as non-scalar: %s", asg0.name); + unqueue_signals(); + return 1; + } else if (!strcmp(asg->name, pm->ename)) { + already_tied = 1; + } else { + zwarnnam(name, "can't tie already tied scalar: %s", + asg0.name); + unqueue_signals(); + return 1; + } + } else { /* - * Already tied in the fashion requested. + * Variable already exists in the current scope but is not tied. + * We're preserving its value and export attribute but no other + * attributes upon converting to "tied". */ - struct tieddata *tdp = (struct tieddata*)pm->u.data; - int flags = (asg->flags & ASG_KEY_VALUE) ? - ASSPM_KEY_VALUE : 0; - /* Update join character */ - tdp->joinchar = joinchar; - if (asg0.value.scalar) - assignsparam(asg0.name, ztrdup(asg0.value.scalar), 0); - else if (asg->value.array) - assignaparam( - asg->name, zlinklist2array(asg->value.array),flags); - return 0; - } else { - zwarnnam(name, "can't tie already tied scalar: %s", - asg0.name); + if (!asg0.value.scalar && !asg->value.array && + !(PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED))) + oldval = ztrdup(getsparam(asg0.name)); + on |= (pm->node.flags & ~roff) & PM_EXPORTED; } - return 1; } - if (!asg0.value.scalar && !asg->value.array && - !(PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED))) - oldval = ztrdup(getsparam(asg0.name)); - on |= (pm->node.flags & PM_EXPORTED); + } + if (already_tied) { + int ret; + /* + * If already tied, we still need to call typeset_single on + * both the array and colonarray, if only to update the attributes + * of both, and of course to set the new value if one is provided + * for either of them. + */ + ret = !(typeset_single(name, asg0.name, pm, + func, on, off, roff, &asg0, apm, + ops, joinchar) && + typeset_single(name, asg->name, apm, + func, (on | PM_ARRAY) & ~PM_EXPORTED, + off & ~PM_ARRAY, roff, asg, NULL, ops, 0) + ); + unqueue_signals(); + return ret; } /* * Create the tied array; this is normal except that @@ -2832,9 +2903,7 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) * Create the tied colonarray. We make it as a normal scalar * and fix up the oddities later. */ - if (!(pm=typeset_single(name, asg0.name, - (Param)paramtab->getnode(paramtab, - asg0.name), + if (!(pm=typeset_single(name, asg0.name, pm, func, on, off, roff, &asg0, apm, ops, joinchar))) { if (oldval) diff --git a/Src/hashtable.h b/Src/hashtable.h index 21398e17c..f6778664e 100644 --- a/Src/hashtable.h +++ b/Src/hashtable.h @@ -63,6 +63,7 @@ #define BIN_UNALIAS 29 #define BIN_UNFUNCTION 30 #define BIN_UNSET 31 +#define BIN_EXPORT 32 /* These currently depend on being 0 and 1. */ #define BIN_SETOPT 0 diff --git a/Src/params.c b/Src/params.c index f7ecff32a..089a958ae 100644 --- a/Src/params.c +++ b/Src/params.c @@ -290,7 +290,7 @@ static initparam special_params[] ={ #define GSU(X) BR((GsuScalar)(void *)(&(X))) #define NULL_GSU BR((GsuScalar)(void *)NULL) #define IPDEF1(A,B,C) {{NULL,A,PM_INTEGER|PM_SPECIAL|C},BR(NULL),GSU(B),10,0,NULL,NULL,NULL,0} -IPDEF1("#", pound_gsu, PM_READONLY), +IPDEF1("#", pound_gsu, PM_READONLY_SPECIAL), IPDEF1("ERRNO", errno_gsu, PM_UNSET), IPDEF1("GID", gid_gsu, PM_DONTIMPORT | PM_RESTRICTED), IPDEF1("EGID", egid_gsu, PM_DONTIMPORT | PM_RESTRICTED), @@ -300,11 +300,11 @@ IPDEF1("SAVEHIST", savehist_gsu, PM_RESTRICTED), IPDEF1("SECONDS", intseconds_gsu, 0), IPDEF1("UID", uid_gsu, PM_DONTIMPORT | PM_RESTRICTED), IPDEF1("EUID", euid_gsu, PM_DONTIMPORT | PM_RESTRICTED), -IPDEF1("TTYIDLE", ttyidle_gsu, PM_READONLY), +IPDEF1("TTYIDLE", ttyidle_gsu, PM_READONLY_SPECIAL), #define IPDEF2(A,B,C) {{NULL,A,PM_SCALAR|PM_SPECIAL|C},BR(NULL),GSU(B),0,0,NULL,NULL,NULL,0} IPDEF2("USERNAME", username_gsu, PM_DONTIMPORT|PM_RESTRICTED), -IPDEF2("-", dash_gsu, PM_READONLY), +IPDEF2("-", dash_gsu, PM_READONLY_SPECIAL), IPDEF2("histchars", histchars_gsu, PM_DONTIMPORT), IPDEF2("HOME", home_gsu, PM_UNSET), IPDEF2("TERM", term_gsu, PM_UNSET), @@ -337,7 +337,7 @@ LCIPDEF("LC_TIME"), # endif #endif /* USE_LOCALE */ -#define IPDEF4(A,B) {{NULL,A,PM_INTEGER|PM_READONLY|PM_SPECIAL},BR((void *)B),GSU(varint_readonly_gsu),10,0,NULL,NULL,NULL,0} +#define IPDEF4(A,B) {{NULL,A,PM_INTEGER|PM_READONLY_SPECIAL},BR((void *)B),GSU(varint_readonly_gsu),10,0,NULL,NULL,NULL,0} IPDEF4("!", &lastpid), IPDEF4("$", &mypid), IPDEF4("?", &lastval), @@ -377,10 +377,9 @@ IPDEF7("PS3", &prompt3), IPDEF7R("PS4", &prompt4), IPDEF7("SPROMPT", &sprompt), -#define IPDEF9F(A,B,C,D) {{NULL,A,D|PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT},BR((void *)B),GSU(vararray_gsu),0,0,NULL,C,NULL,0} -#define IPDEF9(A,B,C) IPDEF9F(A,B,C,0) -IPDEF9F("*", &pparams, NULL, PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT|PM_READONLY), -IPDEF9F("@", &pparams, NULL, PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT|PM_READONLY), +#define IPDEF9(A,B,C,D) {{NULL,A,D|PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT},BR((void *)B),GSU(vararray_gsu),0,0,NULL,C,NULL,0} +IPDEF9("*", &pparams, NULL, PM_ARRAY|PM_READONLY_SPECIAL|PM_DONTIMPORT), +IPDEF9("@", &pparams, NULL, PM_ARRAY|PM_READONLY_SPECIAL|PM_DONTIMPORT), /* * This empty row indicates the end of parameters available in @@ -389,17 +388,17 @@ IPDEF9F("@", &pparams, NULL, PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT|PM_READONLY), {{NULL,NULL,0},BR(NULL),NULL_GSU,0,0,NULL,NULL,NULL,0}, #define IPDEF8(A,B,C,D) {{NULL,A,D|PM_SCALAR|PM_SPECIAL},BR((void *)B),GSU(colonarr_gsu),0,0,NULL,C,NULL,0} -IPDEF8("CDPATH", &cdpath, "cdpath", 0), -IPDEF8("FIGNORE", &fignore, "fignore", 0), -IPDEF8("FPATH", &fpath, "fpath", 0), -IPDEF8("MAILPATH", &mailpath, "mailpath", 0), -IPDEF8("WATCH", &watch, "watch", 0), -IPDEF8("PATH", &path, "path", PM_RESTRICTED), -IPDEF8("PSVAR", &psvar, "psvar", 0), -IPDEF8("ZSH_EVAL_CONTEXT", &zsh_eval_context, "zsh_eval_context", PM_READONLY), +IPDEF8("CDPATH", &cdpath, "cdpath", PM_TIED), +IPDEF8("FIGNORE", &fignore, "fignore", PM_TIED), +IPDEF8("FPATH", &fpath, "fpath", PM_TIED), +IPDEF8("MAILPATH", &mailpath, "mailpath", PM_TIED), +IPDEF8("WATCH", &watch, "watch", PM_TIED), +IPDEF8("PATH", &path, "path", PM_RESTRICTED|PM_TIED), +IPDEF8("PSVAR", &psvar, "psvar", PM_TIED), +IPDEF8("ZSH_EVAL_CONTEXT", &zsh_eval_context, "zsh_eval_context", PM_READONLY_SPECIAL|PM_TIED), /* MODULE_PATH is not imported for security reasons */ -IPDEF8("MODULE_PATH", &module_path, "module_path", PM_DONTIMPORT|PM_RESTRICTED), +IPDEF8("MODULE_PATH", &module_path, "module_path", PM_DONTIMPORT|PM_RESTRICTED|PM_TIED), #define IPDEF10(A,B) {{NULL,A,PM_ARRAY|PM_SPECIAL},BR(NULL),GSU(B),10,0,NULL,NULL,NULL,0} @@ -409,7 +408,7 @@ IPDEF8("MODULE_PATH", &module_path, "module_path", PM_DONTIMPORT|PM_RESTRICTED), */ /* All of these have sh compatible equivalents. */ -IPDEF1("ARGC", argc_gsu, PM_READONLY), +IPDEF1("ARGC", argc_gsu, PM_READONLY_SPECIAL), IPDEF2("HISTCHARS", histchars_gsu, PM_DONTIMPORT), IPDEF4("status", &lastval), IPDEF7("prompt", &prompt), @@ -417,20 +416,20 @@ IPDEF7("PROMPT", &prompt), IPDEF7("PROMPT2", &prompt2), IPDEF7("PROMPT3", &prompt3), IPDEF7("PROMPT4", &prompt4), -IPDEF8("MANPATH", &manpath, "manpath", 0), -IPDEF9("argv", &pparams, NULL), -IPDEF9("fignore", &fignore, "FIGNORE"), -IPDEF9("cdpath", &cdpath, "CDPATH"), -IPDEF9("fpath", &fpath, "FPATH"), -IPDEF9("mailpath", &mailpath, "MAILPATH"), -IPDEF9("manpath", &manpath, "MANPATH"), -IPDEF9("psvar", &psvar, "PSVAR"), -IPDEF9("watch", &watch, "WATCH"), +IPDEF8("MANPATH", &manpath, "manpath", PM_TIED), +IPDEF9("argv", &pparams, NULL, 0), +IPDEF9("fignore", &fignore, "FIGNORE", PM_TIED), +IPDEF9("cdpath", &cdpath, "CDPATH", PM_TIED), +IPDEF9("fpath", &fpath, "FPATH", PM_TIED), +IPDEF9("mailpath", &mailpath, "MAILPATH", PM_TIED), +IPDEF9("manpath", &manpath, "MANPATH", PM_TIED), +IPDEF9("psvar", &psvar, "PSVAR", PM_TIED), +IPDEF9("watch", &watch, "WATCH", PM_TIED), -IPDEF9F("zsh_eval_context", &zsh_eval_context, "ZSH_EVAL_CONTEXT", PM_READONLY), +IPDEF9("zsh_eval_context", &zsh_eval_context, "ZSH_EVAL_CONTEXT", PM_TIED|PM_READONLY_SPECIAL), -IPDEF9F("module_path", &module_path, "MODULE_PATH", PM_RESTRICTED), -IPDEF9F("path", &path, "PATH", PM_RESTRICTED), +IPDEF9("module_path", &module_path, "MODULE_PATH", PM_TIED|PM_RESTRICTED), +IPDEF9("path", &path, "PATH", PM_TIED|PM_RESTRICTED), /* These are known to zsh alone. */ @@ -451,7 +450,7 @@ IPDEF8("MAILPATH", &mailpath, NULL, 0), IPDEF8("WATCH", &watch, NULL, 0), IPDEF8("PATH", &path, NULL, PM_RESTRICTED), IPDEF8("PSVAR", &psvar, NULL, 0), -IPDEF8("ZSH_EVAL_CONTEXT", &zsh_eval_context, NULL, PM_READONLY), +IPDEF8("ZSH_EVAL_CONTEXT", &zsh_eval_context, NULL, PM_READONLY_SPECIAL), /* MODULE_PATH is not imported for security reasons */ IPDEF8("MODULE_PATH", &module_path, NULL, PM_DONTIMPORT|PM_RESTRICTED), @@ -464,7 +463,7 @@ IPDEF8("MODULE_PATH", &module_path, NULL, PM_DONTIMPORT|PM_RESTRICTED), * and $@, this is not readonly. This parameter is not directly * visible in user space. */ -static initparam argvparam_pm = IPDEF9F("", &pparams, NULL, \ +static initparam argvparam_pm = IPDEF9("", &pparams, NULL, \ PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT); #undef BR @@ -5024,10 +5023,10 @@ arrfixenv(char *s, char **t) if (!(pm->node.flags & PM_EXPORTED)) return; - if (pm->node.flags & PM_TIED) - joinchar = STOUC(((struct tieddata *)pm->u.data)->joinchar); - else + if (pm->node.flags & PM_SPECIAL) joinchar = ':'; + else + joinchar = STOUC(((struct tieddata *)pm->u.data)->joinchar); addenv(pm, t ? zjoin(t, joinchar, 1) : ""); } @@ -5650,7 +5649,7 @@ freeparamnode(HashNode hn) pm->gsu.s->unsetfn(pm, 1); zsfree(pm->node.nam); /* If this variable was tied by the user, ename was ztrdup'd */ - if (pm->node.flags & PM_TIED) + if (!(pm->node.flags & PM_SPECIAL)) zsfree(pm->ename); zfree(pm, sizeof(struct param)); } @@ -5685,7 +5684,9 @@ static const struct paramtypes pmtypes[] = { { PM_UPPER, "uppercase", 'u', 0}, { PM_READONLY, "readonly", 'r', 0}, { PM_TAGGED, "tagged", 't', 0}, - { PM_EXPORTED, "exported", 'x', 0} + { PM_EXPORTED, "exported", 'x', 0}, + { PM_UNIQUE, "unique", 'U', 0}, + { PM_TIED, "tied", 'T', 0} }; #define PMTYPES_SIZE ((int)(sizeof(pmtypes)/sizeof(struct paramtypes))) @@ -5774,10 +5775,6 @@ printparamvalue(Param p, int printflags) } break; } - if ((printflags & (PRINT_KV_PAIR|PRINT_LINE)) == PRINT_KV_PAIR) - putchar(' '); - else if (!(printflags & PRINT_KV_PAIR)) - putchar('\n'); } /**/ @@ -5785,36 +5782,41 @@ mod_export void printparamnode(HashNode hn, int printflags) { Param p = (Param) hn; + Param peer = NULL; if (p->node.flags & PM_UNSET) { - if (isset(POSIXBUILTINS) && (p->node.flags & PM_READONLY) && - (printflags & PRINT_TYPESET)) - { + if (printflags & (PRINT_POSIX_READONLY|PRINT_POSIX_EXPORT) && + p->node.flags & (PM_READONLY|PM_EXPORTED)) { /* - * Special POSIX rules: show the parameter as readonly + * Special POSIX rules: show the parameter as readonly/exported * even though it's unset, but with no value. */ printflags |= PRINT_NAMEONLY; } - else if (p->node.flags & PM_EXPORTED) - printflags |= PRINT_NAMEONLY; else return; } if (p->node.flags & PM_AUTOLOAD) printflags |= PRINT_NAMEONLY; - if (printflags & PRINT_TYPESET) { - if ((p->node.flags & (PM_READONLY|PM_SPECIAL)) == - (PM_READONLY|PM_SPECIAL) || - (p->node.flags & PM_AUTOLOAD)) { + if (printflags & (PRINT_TYPESET|PRINT_POSIX_READONLY|PRINT_POSIX_EXPORT)) { + if (p->node.flags & (PM_RO_BY_DESIGN|PM_AUTOLOAD)) { /* * It's not possible to restore the state of * these, so don't output. */ return; } - if (locallevel && p->level >= locallevel) { + /* + * The zsh variants of export -p/readonly -p also report other + * flags to indicate other attributes or scope. The POSIX variants + * don't. + */ + if (printflags & PRINT_POSIX_EXPORT) { + printf("export "); + } else if (printflags & PRINT_POSIX_READONLY) { + printf("readonly "); + } else if (locallevel && p->level >= locallevel) { printf("typeset "); /* printf("local "); */ } else if ((p->node.flags & PM_EXPORTED) && !(p->node.flags & (PM_ARRAY|PM_HASHED))) { @@ -5861,22 +5863,48 @@ printparamnode(HashNode hn, int printflags) } } } - if (p->node.flags & PM_UNIQUE) { - if (!doneminus) { - putchar('-'); - doneminus = 1; - } - putchar('U'); - } if (doneminus) putchar(' '); + + if (p->node.flags & PM_TIED) { + /* + * For scalars tied to arrays,s + * * typeset +m outputs + * array tied SCALAR array + * tied array SCALAR + * * typeset -p outputs: + * typeset -T SCALAR array (for hidden values) + * typeset -T SCALAR array=(values) + * for both scalar and array (flags may be different) + * + * We choose to print the value for the array instead of the scalar + * as scalars can't disambiguate between + * typeset -T SCALAR array=() + * and + * typeset -T SCALAR array=('') + * (same for (a b:c)...) + */ + Param tmp = (Param) paramtab->getnode(paramtab, p->ename); + + /* + * Swap param and tied peer for typeset -p output + */ + if (!(printflags & PRINT_TYPESET) || (p->node.flags & PM_ARRAY)) + peer = tmp; + else { + peer = p; + p = tmp; + } + + quotedzputs(peer->node.nam, stdout); + putchar(' '); + } } if ((printflags & PRINT_NAMEONLY) || - ((p->node.flags & PM_HIDEVAL) && !(printflags & PRINT_INCLUDEVALUE))) { - zputs(p->node.nam, stdout); - putchar('\n'); - } else { + ((p->node.flags & PM_HIDEVAL) && !(printflags & PRINT_INCLUDEVALUE))) + quotedzputs(p->node.nam, stdout); + else { if (printflags & PRINT_KV_PAIR) { if (printflags & PRINT_LINE) printf("\n "); @@ -5888,4 +5916,22 @@ printparamnode(HashNode hn, int printflags) printparamvalue(p, printflags); } + if (peer && (printflags & PRINT_TYPESET) && !(p->node.flags & PM_SPECIAL)) { + /* + * append the join char for tied parameters if different from colon + * for typeset -p output. + */ + unsigned char joinchar = STOUC(((struct tieddata *)peer->u.data)->joinchar); + if (joinchar != ':') { + char buf[2]; + buf[0] = joinchar; + buf[1] = '\0'; + putchar(' '); + quotedzputs(buf, stdout); + } + } + if ((printflags & (PRINT_KV_PAIR|PRINT_LINE)) == PRINT_KV_PAIR) + putchar(' '); + else if (!(printflags & PRINT_KV_PAIR)) + putchar('\n'); } diff --git a/Src/subst.c b/Src/subst.c index c1021fbf3..c706b9688 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -2552,8 +2552,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, val = dyncat(val, "-readonly"); if (f & PM_TAGGED) val = dyncat(val, "-tag"); - if (f & PM_TAGGED_LOCAL) - val = dyncat(val, "-tag_local"); + if (f & PM_TIED) + val = dyncat(val, "-tied"); if (f & PM_EXPORTED) val = dyncat(val, "-export"); if (f & PM_UNIQUE) diff --git a/Src/zsh.h b/Src/zsh.h index b81db1527..8d39a0493 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -1886,18 +1886,21 @@ struct tieddata { #define PM_ANONYMOUS (1<<20) /* (function) anonymous function */ #define PM_LOCAL (1<<21) /* this parameter will be made local */ #define PM_SPECIAL (1<<22) /* special builtin parameter */ -#define PM_DONTIMPORT (1<<23) /* do not import this variable */ -#define PM_RESTRICTED (1<<24) /* cannot be changed in restricted mode */ -#define PM_UNSET (1<<25) /* has null value */ -#define PM_REMOVABLE (1<<26) /* special can be removed from paramtab */ -#define PM_AUTOLOAD (1<<27) /* autoloaded from module */ -#define PM_NORESTORE (1<<28) /* do not restore value of local special */ -#define PM_AUTOALL (1<<28) /* autoload all features in module +#define PM_RO_BY_DESIGN (1<<23) /* to distinguish from specials that can be + made read-only by the user */ +#define PM_READONLY_SPECIAL (PM_SPECIAL|PM_READONLY|PM_RO_BY_DESIGN) +#define PM_DONTIMPORT (1<<24) /* do not import this variable */ +#define PM_RESTRICTED (1<<25) /* cannot be changed in restricted mode */ +#define PM_UNSET (1<<26) /* has null value */ +#define PM_REMOVABLE (1<<27) /* special can be removed from paramtab */ +#define PM_AUTOLOAD (1<<28) /* autoloaded from module */ +#define PM_NORESTORE (1<<29) /* do not restore value of local special */ +#define PM_AUTOALL (1<<29) /* autoload all features in module * when loading: valid only if PM_AUTOLOAD * is also present. */ -#define PM_HASHELEM (1<<29) /* is a hash-element */ -#define PM_NAMEDDIR (1<<30) /* has a corresponding nameddirtab entry */ +#define PM_HASHELEM (1<<30) /* is a hash-element */ +#define PM_NAMEDDIR (1<<31) /* has a corresponding nameddirtab entry */ /* The option string corresponds to the first of the variables above */ #define TYPESET_OPTSTR "aiEFALRZlurtxUhHTkz" @@ -2138,6 +2141,8 @@ typedef groupset *Groupset; #define PRINT_INCLUDEVALUE (1<<4) #define PRINT_TYPESET (1<<5) #define PRINT_LINE (1<<6) +#define PRINT_POSIX_EXPORT (1<<7) +#define PRINT_POSIX_READONLY (1<<8) /* flags for printing for the whence builtin */ #define PRINT_WHENCE_CSH (1<<7) diff --git a/Test/B02typeset.ztst b/Test/B02typeset.ztst index b53f42f83..ac86e0ad1 100644 --- a/Test/B02typeset.ztst +++ b/Test/B02typeset.ztst @@ -20,6 +20,14 @@ # Not yet tested: # Assorted illegal flag combinations +# For a few tests, we include a +# typeset -p param +# typeset -m param +# typeset +m param +# to test the proper output of typeset for a number of different types +# of variables. Note that we can't use a dedicated function to factorize +# that code, as that would affect the scoping. + %prep ## Do not remove the next line, it's used by V10private.ztst # test_zsh_param_private @@ -37,6 +45,9 @@ typeset -a array array=(l o c a l) print $scalar $array + typeset -p scalar array + typeset -m scalar array + typeset +m scalar array } scope01() { local scalar @@ -44,6 +55,9 @@ local -a array array=(l o c a l) print $scalar $array + typeset -p scalar array + typeset -m scalar array + typeset +m scalar array } scope02() { declare scalar @@ -51,10 +65,16 @@ declare -a array array=(l o c a l) print $scalar $array + typeset -p scalar array + typeset -m scalar array + typeset +m scalar array } scope10() { export outer=outer /bin/sh -fc 'echo $outer' + typeset -p outer + typeset -m outer + typeset +m outer } scope11() { typeset -x outer=outer @@ -68,6 +88,9 @@ local -xT OUTER outer outer=(i n n e r) /bin/sh -fc 'echo $OUTER' + typeset -p OUTER outer + typeset -m OUTER outer + typeset +m OUTER outer } # Bug? `typeset -h' complains that ! # $ * - ? @ are not identifiers. @@ -79,8 +102,14 @@ %test + typeset -p scalar array + typeset -m scalar array typeset +m scalar array -0:Report types of parameters with typeset +m +0:Report types for global variables +>typeset -g scalar=scalar +>typeset -g -a array=( a r r a y ) +>scalar=scalar +>array=( a r r a y ) >scalar >array array @@ -88,18 +117,36 @@ print $scalar $array 0:Simple local declarations >local l o c a l +>typeset scalar=local +>typeset -a array=( l o c a l ) +>scalar=local +>array=( l o c a l ) +>local scalar +>array local array >scalar a r r a y scope01 print $scalar $array 0:Equivalence of local and typeset in functions >local l o c a l +>typeset scalar=local +>typeset -a array=( l o c a l ) +>scalar=local +>array=( l o c a l ) +>local scalar +>array local array >scalar a r r a y scope02 print $scalar $array 0:Basic equivalence of declare and typeset >local l o c a l +>typeset scalar=local +>typeset -a array=( l o c a l ) +>scalar=local +>array=( l o c a l ) +>local scalar +>array local array >scalar a r r a y declare +m scalar @@ -110,6 +157,9 @@ print $outer 0:Global export >outer +>export outer=outer +>outer=outer +>outer >outer scope11 @@ -130,18 +180,30 @@ print $f float -F f print $f + typeset -p f + typeset -m f + typeset +m f 0:Floating point, adding a precision, and fixed point >float local f >3.14e+00 >3.142 +>typeset -F f=3.142 +>f=3.142 +>float local f integer i=3.141 typeset +m i integer -i2 i print $i + typeset -p i + typeset -m i + typeset +m i 0:Integer and changing the base >integer local i >2#11 +>typeset -i2 i=3 +>i=3 +>integer 2 local i float -E3 f=3.141 typeset +m f @@ -174,16 +236,33 @@ typeset -gU array print $array + typeset -p array + typeset -m array + typeset +m array 0:Uniquified arrays and non-local scope >a r y +>typeset -g -aU array=( a r y ) +>array=( a r y ) +>array unique array typeset -T SCALAR=l:o:c:a:l array print $array typeset -U SCALAR print $SCALAR $array + typeset -p SCALAR array + typeset -m SCALAR array + typeset +m SCALAR array + print ${(t)SCALAR} ${(t)array} 0:Tied parameters and uniquified colon-arrays >l o c a l >l:o:c:a l o c a +>typeset -UT SCALAR array=( l o c a ) +>typeset -aT SCALAR array=( l o c a ) +>SCALAR=l:o:c:a +>array=( l o c a ) +>local unique tied array SCALAR +>array local tied SCALAR array +>scalar-local-tied-unique array-local-tied (setopt NO_multibyte cbases LC_ALL=C 2>/dev/null @@ -209,9 +288,18 @@ typeset -T SCALAR=$'l\000o\000c\000a\000l' array $'\000' typeset -U SCALAR print $array + typeset -p SCALAR array + typeset -m SCALAR array + typeset +m SCALAR array [[ $SCALAR == $'l\000o\000c\000a' ]] 0:Tied parameters and uniquified arrays with NUL-character as separator >l o c a +>typeset -UT SCALAR array=( l o c a ) '' +>typeset -aT SCALAR array=( l o c a ) '' +>SCALAR=$'l\C-@o\C-@c\C-@a' +>array=( l o c a ) +>local unique tied array SCALAR +>array local tied SCALAR array typeset -T SCALAR array typeset +T SCALAR @@ -223,15 +311,30 @@ print $OUTER 0:Export of tied parameters >i:n:n:e:r +>typeset -xT OUTER outer=( i n n e r ) +>typeset -aT OUTER outer=( i n n e r ) +>OUTER=i:n:n:e:r +>outer=( i n n e r ) +>local exported tied outer OUTER +>array local tied OUTER outer >outer typeset -TU MORESTUFF=here-we-go-go-again morestuff '-' print -l $morestuff + typeset -p MORESTUFF morestuff + typeset -m MORESTUFF morestuff + typeset +m MORESTUFF morestuff 0:Tied arrays with separator specified >here >we >go >again +>typeset -UT MORESTUFF morestuff=( here we go again ) - +>typeset -aUT MORESTUFF morestuff=( here we go again ) - +>MORESTUFF=here-we-go-again +>morestuff=( here we go again ) +>local unique tied morestuff MORESTUFF +>array local unique tied MORESTUFF morestuff typeset -T THIS will not work 1:Tied array syntax @@ -251,13 +354,25 @@ local b=1 ;: to stomp assoc[1] if assoc[b] is broken typeset assoc[1]=a assoc[b]=2 assoc[3]=c print $assoc[1] $assoc[b] $assoc[3] + typeset -p assoc + typeset -m assoc + typeset +m assoc 0:Legal local associative array element assignment >a 2 c +>typeset -A assoc=( [1]=a [3]=c [b]=2 ) +>assoc=( [1]=a [3]=c [b]=2 ) +>association local assoc local scalar scalar[1]=a scalar[2]=b scalar[3]=c print $scalar + typeset -p scalar + typeset -m scalar + typeset +m scalar 0:Local scalar subscript assignment >abc +>typeset scalar=abc +>scalar=abc +>local scalar typeset -L 10 fools for fools in " once" "twice" " thrice" " oops too long here"; do @@ -273,11 +388,17 @@ for foolf in 1.3 4.6 -2.987 -4.91031; do print "'$foolf'" done + typeset -p foolf + typeset -m foolf + typeset +m foolf 0:Left justification of floating point >'1.300 ' >'4.600 ' >'-2.987 ' >'-4.910 ' +>typeset -FL10 foolf=-4.910 +>foolf=-4.910 +>float local left justified 10 foolf typeset -L 10 -Z foolzs for foolzs in 001.3 04.6 -2.987 -04.91231; do @@ -293,10 +414,16 @@ for foors in short longer even-longer; do print "'$foors'" done + typeset -p foors + typeset -m foors + typeset +m foors 0:Right justification of scalars >' short' >' longer' >'ven-longer' +>typeset -R10 foors=even-longer +>foors=even-longer +>local right justified 10 foors typeset -Z 10 foozs for foozs in 42 -42 " 43" " -43"; do @@ -436,24 +563,36 @@ print $case1 upper="VALUE OF \$UPPER" print ${(P)case1} + typeset -p case1 + typeset -m case1 + typeset +m case1 0:Upper case conversion, does not apply to values used internally >UPPER >VALUE OF $UPPER +>typeset -u case1=upper +>case1=upper +>local uppercase case1 local case2=LOWER typeset -l case2 print $case2 LOWER="value of \$lower" print ${(P)case2} + typeset -p case2 + typeset -m case2 + typeset +m case2 0:Lower case conversion, does not apply to values used internally >lower >value of $lower +>typeset -l case2=LOWER +>case2=LOWER +>local lowercase case2 typeset -a array array=(foo bar) fn() { typeset -p array nonexistent; } fn -1:declare -p shouldn't create scoped values +1:typeset -p shouldn't create scoped values >typeset -g -a array=( foo bar ) ?fn:typeset: no such variable: nonexistent @@ -490,7 +629,7 @@ ?0 ?(eval):5: read-only variable: pbro ?(eval):6: read-only variable: pbro -?typeset -g -r pbro +?readonly pbro ?0 ?(eval):10: read-only variable: pbro @@ -820,7 +959,145 @@ > [three]='' >) - (typeset -a -U foo=(bar bar) - typeset -p foo) -0:typeset -p of typeset -U ->typeset -aU foo=( bar ) + (export PATH MANPATH + path=(/bin) + MANPATH=/ + # read-only special params like zsh_eval_context are not output by typeset -p + specials=(path PATH manpath MANPATH zsh_eval_context ZSH_EVAL_CONTEXT) + typeset -p $specials + typeset -m $specials + typeset +m $specials + for var ($specials) print $var: ${(Pt)var} + ) +0:typeset output for some special tied parameters +>typeset -g -aT PATH path=( /bin ) +>export -T PATH path=( /bin ) +>typeset -g -aT MANPATH manpath=( / ) +>export -T MANPATH manpath=( / ) +>path=( /bin ) +>PATH=/bin +>manpath=( / ) +>MANPATH=/ +>zsh_eval_context=( toplevel shfunc shfunc shfunc eval ) +>ZSH_EVAL_CONTEXT=toplevel:shfunc:shfunc:shfunc:eval +>array tied PATH path +>tied path PATH +>array tied MANPATH manpath +>tied manpath MANPATH +>array readonly tied ZSH_EVAL_CONTEXT zsh_eval_context +>readonly tied zsh_eval_context ZSH_EVAL_CONTEXT +>path: array-tied-special +>PATH: scalar-tied-export-special +>manpath: array-tied-special +>MANPATH: scalar-tied-export-special +>zsh_eval_context: array-readonly-tied-special +>ZSH_EVAL_CONTEXT: scalar-readonly-tied-special + + typeset -T VAR var=(a b a b) + typeset -UuT VAR var + + print $VAR +0:redeclare a tied variable with different attributes +>A+B + + typeset -T VAR=a+b var + typeset -T VAR var + + print $var +0:colonarray re-split when changing the join character +>a b + + readonly -T VAR var=(a b) + readonly -T VAR var + +1:cannot change the join character on a readonly tied variable +?(eval):1: read-only variable: var + + typeset -T FOO manpath +1:Can't tie a special tied array to a different variable +?(eval):typeset:1: manpath special parameter can only be tied to special parameter MANPATH + + typeset -T MANPATH foo +1:Can't tie a special tied scalar to a different variable +?(eval):typeset:1: MANPATH special parameter can only be tied to special parameter manpath + + typeset -T MANPATH manpath + +1:Can't change the join character of a special tied variable +?(eval):typeset:1: cannot change the join character of special tied parameters + + (){ + typeset -h path + typeset -T PATH path=(x) + } + (){ + typeset -h PATH + typeset -T PATH path=(x) + } +1:reject attempt to tie special to downgraded peer +?(anon):typeset:2: PATH special parameter can only be tied to special parameter path +?(anon):typeset:2: path special parameter can only be tied to special parameter PATH + + typeset MANPATH + manpath=(/ /) + typeset -UT MANPATH manpath + print $manpath +0:OK to run typeset -T on tied specials as long as peer and joinchar are unchanged +>/ + + typeset FOO=a:b + export FOO + typeset +x -T FOO foo + typeset -p FOO +0:Make sure +x is honoured when tying a parameter +>typeset -T FOO foo=( a b ) + + $ZTST_testdir/../Src/zsh --emulate sh -f -c ' + PATH=/bin; export PATH; readonly PATH + export -p PATH + typeset -p PATH + readonly -p' +0: readonly/export output for exported+readonly+special when started as sh +>export PATH=/bin +>export -r PATH=/bin +>readonly PATH=/bin + + function { + emulate -L sh + MANPATH=/bin; export MANPATH; readonly MANPATH + export -p MANPATH + typeset -p MANPATH + readonly -p + } +0: readonly/export output for exported+readonly+tied+special after switching to sh emulation +>export MANPATH=/bin +>export -rT MANPATH manpath=( /bin ) +>readonly MANPATH=/bin + + function { + local -rax zsh_exported_readonly_array=(2) + local -rAx zsh_exported_readonly_hash=(3 3) + local -rx zsh_exported_readonly_scalar=1 + print zsh: + export -p | grep zsh_exported_readonly + readonly -p | grep zsh_exported_readonly + print sh: + emulate -L sh + export -p | grep zsh_exported_readonly + readonly -p | grep zsh_exported_readonly + print still asking for arrays: + export -ap | grep zsh_exported_readonly + readonly -ap | grep zsh_exported_readonly + } +0: no array/hash in POSIX export/readonly -p +>zsh: +>typeset -arx zsh_exported_readonly_array=( 2 ) +>typeset -Arx zsh_exported_readonly_hash=( [3]=3 ) +>typeset -rx zsh_exported_readonly_scalar=1 +>typeset -arx zsh_exported_readonly_array=( 2 ) +>typeset -Arx zsh_exported_readonly_hash=( [3]=3 ) +>typeset -rx zsh_exported_readonly_scalar=1 +>sh: +>export zsh_exported_readonly_scalar=1 +>readonly zsh_exported_readonly_scalar=1 +>still asking for arrays: +>export zsh_exported_readonly_array=( 2 ) +>export zsh_exported_readonly_scalar=1 +>readonly zsh_exported_readonly_array=( 2 ) +>readonly zsh_exported_readonly_scalar=1