From f9ffde8e3c19cb9606f8ae4a7e098be38e807542 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Wed, 16 Mar 2005 11:51:37 +0000 Subject: 20983: failed glob from fixed strings segments in globbed paths --- Src/Modules/parameter.c | 928 ++++++++++++---------- Src/glob.c | 1468 +++++++++++++++++++--------------- Src/params.c | 2015 +++++++++++++++++++++++++++++++++-------------- 3 files changed, 2778 insertions(+), 1633 deletions(-) (limited to 'Src') diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c index 5dce2f958..7005501a8 100644 --- a/Src/Modules/parameter.c +++ b/Src/Modules/parameter.c @@ -51,13 +51,12 @@ createspecialhash(char *name, GetNodeFunc get, ScanTabFunc scan) Param pm; HashTable ht; - if (!(pm = createparam(name, PM_SPECIAL|PM_HIDE|PM_REMOVABLE|PM_HASHED))) + if (!(pm = createparam(name, PM_SPECIAL|PM_HIDE|PM_HIDEVAL| + PM_REMOVABLE|PM_HASHED))) return NULL; pm->level = pm->old ? locallevel : 0; - pm->gets.hfn = hashgetfn; - pm->sets.hfn = hashsetfn; - pm->unsetfn = stdunsetfn; + pm->gsu.h = &stdhash_gsu; pm->u.hash = ht = newhashtable(0, name, NULL); ht->hash = hasher; @@ -122,6 +121,8 @@ paramtypestr(Param pm) val = dyncat(val, "-unique"); if (f & PM_HIDE) val = dyncat(val, "-hide"); + if (f & PM_HIDEVAL) + val = dyncat(val, "-hideval"); if (f & PM_SPECIAL) val = dyncat(val, "-special"); } else @@ -132,21 +133,14 @@ paramtypestr(Param pm) /**/ static HashNode -getpmparameter(HashTable ht, char *name) +getpmparameter(UNUSED(HashTable ht), char *name) { Param rpm, pm = NULL; - pm = (Param) zhalloc(sizeof(struct param)); + pm = (Param) hcalloc(sizeof(struct param)); pm->nam = dupstring(name); pm->flags = PM_SCALAR | PM_READONLY; - pm->sets.cfn = NULL; - pm->gets.cfn = strgetfn; - pm->unsetfn = NULL; - pm->ct = 0; - pm->env = NULL; - pm->ename = NULL; - pm->old = NULL; - pm->level = 0; + pm->gsu.s = &nullsetscalar_gsu; if ((rpm = (Param) realparamtab->getnode(realparamtab, name)) && !(rpm->flags & PM_UNSET)) pm->u.str = paramtypestr(rpm); @@ -159,24 +153,20 @@ getpmparameter(HashTable ht, char *name) /**/ static void -scanpmparameters(HashTable ht, ScanFunc func, int flags) +scanpmparameters(UNUSED(HashTable ht), ScanFunc func, int flags) { struct param pm; int i; HashNode hn; + memset((void *)&pm, 0, sizeof(struct param)); pm.flags = PM_SCALAR | PM_READONLY; - pm.sets.cfn = NULL; - pm.gets.cfn = strgetfn; - pm.unsetfn = NULL; - pm.ct = 0; - pm.env = NULL; - pm.ename = NULL; - pm.old = NULL; - pm.level = 0; + pm.gsu.s = &nullsetscalar_gsu; for (i = 0; i < realparamtab->hsize; i++) for (hn = realparamtab->nodes[i]; hn; hn = hn->next) { + if (((Param)hn)->flags & PM_UNSET) + continue; pm.nam = hn->nam; if (func != scancountparams && ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) || @@ -192,10 +182,11 @@ scanpmparameters(HashTable ht, ScanFunc func, int flags) static void setpmcommand(Param pm, char *value) { - if (isset(RESTRICTED)) + if (isset(RESTRICTED)) { zwarn("restricted: %s", value, 0); - else { - Cmdnam cn = zcalloc(sizeof(*cn)); + zsfree(value); + } else { + Cmdnam cn = zshcalloc(sizeof(*cn)); cn->flags = HASHED; cn->u.cmd = value; @@ -206,7 +197,7 @@ setpmcommand(Param pm, char *value) /**/ static void -unsetpmcommand(Param pm, int exp) +unsetpmcommand(Param pm, UNUSED(int exp)) { HashNode hn = cmdnamtab->removenode(cmdnamtab, pm->nam); @@ -216,7 +207,7 @@ unsetpmcommand(Param pm, int exp) /**/ static void -setpmcommands(Param pm, HashTable ht) +setpmcommands(UNUSED(Param pm), HashTable ht) { int i; HashNode hn; @@ -226,11 +217,11 @@ setpmcommands(Param pm, HashTable ht) for (i = 0; i < ht->hsize; i++) for (hn = ht->nodes[i]; hn; hn = hn->next) { - Cmdnam cn = zcalloc(sizeof(*cn)); + Cmdnam cn = zshcalloc(sizeof(*cn)); struct value v; - v.isarr = v.inv = v.a = 0; - v.b = -1; + v.isarr = v.inv = v.start = 0; + v.end = -1; v.arr = NULL; v.pm = (Param) hn; @@ -242,9 +233,13 @@ setpmcommands(Param pm, HashTable ht) deleteparamtable(ht); } +static const struct gsu_scalar pmcommand_gsu = +{ strgetfn, setpmcommand, unsetpmcommand }; + + /**/ static HashNode -getpmcommand(HashTable ht, char *name) +getpmcommand(UNUSED(HashTable ht), char *name) { Cmdnam cmd; Param pm = NULL; @@ -254,17 +249,10 @@ getpmcommand(HashTable ht, char *name) cmdnamtab->filltable(cmdnamtab); cmd = (Cmdnam) cmdnamtab->getnode(cmdnamtab, name); } - pm = (Param) zhalloc(sizeof(struct param)); + pm = (Param) hcalloc(sizeof(struct param)); pm->nam = dupstring(name); pm->flags = PM_SCALAR; - pm->sets.cfn = setpmcommand; - pm->gets.cfn = strgetfn; - pm->unsetfn = unsetpmcommand; - pm->ct = 0; - pm->env = NULL; - pm->ename = NULL; - pm->old = NULL; - pm->level = 0; + pm->gsu.s = &pmcommand_gsu; if (cmd) { if (cmd->flags & HASHED) pm->u.str = cmd->u.cmd; @@ -283,7 +271,7 @@ getpmcommand(HashTable ht, char *name) /**/ static void -scanpmcommands(HashTable ht, ScanFunc func, int flags) +scanpmcommands(UNUSED(HashTable ht), ScanFunc func, int flags) { struct param pm; int i; @@ -293,15 +281,9 @@ scanpmcommands(HashTable ht, ScanFunc func, int flags) if (isset(HASHLISTALL)) cmdnamtab->filltable(cmdnamtab); + memset((void *)&pm, 0, sizeof(struct param)); pm.flags = PM_SCALAR; - pm.sets.cfn = setpmcommand; - pm.gets.cfn = strgetfn; - pm.unsetfn = unsetpmcommand; - pm.ct = 0; - pm.env = NULL; - pm.ename = NULL; - pm.old = NULL; - pm.level = 0; + pm.gsu.s = &pmcommand_gsu; for (i = 0; i < cmdnamtab->hsize; i++) for (hn = cmdnamtab->nodes[i]; hn; hn = hn->next) { @@ -337,7 +319,7 @@ setfunction(char *name, char *val, int dis) val = metafy(val, strlen(val), META_REALLOC); - prog = parse_string(val, 1); + prog = parse_string(val); if (!prog || prog == &dummy_eprog) { zwarn("invalid function definition", value, 0); @@ -378,7 +360,7 @@ setpmdisfunction(Param pm, char *value) /**/ static void -unsetpmfunction(Param pm, int exp) +unsetpmfunction(Param pm, UNUSED(int exp)) { HashNode hn = shfunctab->removenode(shfunctab, pm->nam); @@ -388,7 +370,7 @@ unsetpmfunction(Param pm, int exp) /**/ static void -setfunctions(Param pm, HashTable ht, int dis) +setfunctions(UNUSED(Param pm), HashTable ht, int dis) { int i; HashNode hn; @@ -400,8 +382,8 @@ setfunctions(Param pm, HashTable ht, int dis) for (hn = ht->nodes[i]; hn; hn = hn->next) { struct value v; - v.isarr = v.inv = v.a = 0; - v.b = -1; + v.isarr = v.inv = v.start = 0; + v.end = -1; v.arr = NULL; v.pm = (Param) hn; @@ -424,24 +406,22 @@ setpmdisfunctions(Param pm, HashTable ht) setfunctions(pm, ht, DISABLED); } +static const struct gsu_scalar pmfunction_gsu = +{ strgetfn, setpmfunction, unsetpmfunction }; +static const struct gsu_scalar pmdisfunction_gsu = +{ strgetfn, setpmdisfunction, unsetpmfunction }; + /**/ static HashNode -getfunction(HashTable ht, char *name, int dis) +getfunction(UNUSED(HashTable ht), char *name, int dis) { Shfunc shf; Param pm = NULL; - pm = (Param) zhalloc(sizeof(struct param)); + pm = (Param) hcalloc(sizeof(struct param)); pm->nam = dupstring(name); pm->flags = PM_SCALAR; - pm->sets.cfn = (dis ? setpmdisfunction : setpmfunction); - pm->gets.cfn = strgetfn; - pm->unsetfn = unsetpmfunction; - pm->ct = 0; - pm->env = NULL; - pm->ename = NULL; - pm->old = NULL; - pm->level = 0; + pm->gsu.s = dis ? &pmdisfunction_gsu : &pmfunction_gsu; if ((shf = (Shfunc) shfunctab->getnode2(shfunctab, name)) && (dis ? (shf->flags & DISABLED) : !(shf->flags & DISABLED))) { @@ -451,9 +431,18 @@ getfunction(HashTable ht, char *name, int dis) ((shf->flags & PM_TAGGED) ? "Ut" : "U") : ((shf->flags & PM_TAGGED) ? "t" : ""))); } else { - char *t = getpermtext(shf->funcdef, NULL), *h; - - h = dupstring(t); + char *t = getpermtext(shf->funcdef, NULL), *n, *h; + + if (shf->funcdef->flags & EF_RUN) { + n = nicedupstring(name); + h = (char *) zhalloc(strlen(t) + strlen(n) + 9); + h[0] = '\t'; + strcpy(h + 1, t); + strcat(h, "\n\t"); + strcat(h, n); + strcat(h, " \"$@\""); + } else + h = dyncat("\t", t); zsfree(t); unmetafy(h, NULL); @@ -482,21 +471,15 @@ getpmdisfunction(HashTable ht, char *name) /**/ static void -scanfunctions(HashTable ht, ScanFunc func, int flags, int dis) +scanfunctions(UNUSED(HashTable ht), ScanFunc func, int flags, int dis) { struct param pm; int i; HashNode hn; + memset((void *)&pm, 0, sizeof(struct param)); pm.flags = PM_SCALAR; - pm.sets.cfn = (dis ? setpmdisfunction : setpmfunction); - pm.gets.cfn = strgetfn; - pm.unsetfn = unsetpmcommand; - pm.ct = 0; - pm.env = NULL; - pm.ename = NULL; - pm.old = NULL; - pm.level = 0; + pm.gsu.s = dis ? &pmdisfunction_gsu : &pmfunction_gsu; for (i = 0; i < shfunctab->hsize; i++) for (hn = shfunctab->nodes[i]; hn; hn = hn->next) { @@ -513,9 +496,18 @@ scanfunctions(HashTable ht, ScanFunc func, int flags, int dis) ((shf->flags & PM_TAGGED) ? "Ut" : "U") : ((shf->flags & PM_TAGGED) ? "t" : ""))); } else { - char *t = getpermtext(((Shfunc) hn)->funcdef, NULL); - - pm.u.str = dupstring(t); + char *t = getpermtext(((Shfunc) hn)->funcdef, NULL), *n; + + if (((Shfunc) hn)->funcdef->flags & EF_RUN) { + n = nicedupstring(hn->nam); + pm.u.str = (char *) zhalloc(strlen(t) + strlen(n) + 9); + pm.u.str[0] = '\t'; + strcpy(pm.u.str + 1, t); + strcat(pm.u.str, "\n\t"); + strcat(pm.u.str, n); + strcat(pm.u.str, " \"$@\""); + } else + pm.u.str = dyncat("\t", t); unmetafy(pm.u.str, NULL); zsfree(t); } @@ -543,7 +535,7 @@ scanpmdisfunctions(HashTable ht, ScanFunc func, int flags) /**/ static char ** -funcstackgetfn(Param pm) +funcstackgetfn(UNUSED(Param pm)) { Funcstack f; int num; @@ -564,22 +556,15 @@ funcstackgetfn(Param pm) /**/ static HashNode -getbuiltin(HashTable ht, char *name, int dis) +getbuiltin(UNUSED(HashTable ht), char *name, int dis) { Param pm = NULL; Builtin bn; - pm = (Param) zhalloc(sizeof(struct param)); + pm = (Param) hcalloc(sizeof(struct param)); pm->nam = dupstring(name); pm->flags = PM_SCALAR | PM_READONLY; - pm->sets.cfn = NULL; - pm->gets.cfn = strgetfn; - pm->unsetfn = NULL; - pm->ct = 0; - pm->env = NULL; - pm->ename = NULL; - pm->old = NULL; - pm->level = 0; + pm->gsu.s = &nullsetscalar_gsu; if ((bn = (Builtin) builtintab->getnode2(builtintab, name)) && (dis ? (bn->flags & DISABLED) : !(bn->flags & DISABLED))) { char *t = ((bn->handlerfunc || (bn->flags & BINF_PREFIX)) ? @@ -609,21 +594,15 @@ getpmdisbuiltin(HashTable ht, char *name) /**/ static void -scanbuiltins(HashTable ht, ScanFunc func, int flags, int dis) +scanbuiltins(UNUSED(HashTable ht), ScanFunc func, int flags, int dis) { struct param pm; int i; HashNode hn; + memset((void *)&pm, 0, sizeof(struct param)); pm.flags = PM_SCALAR | PM_READONLY; - pm.sets.cfn = NULL; - pm.gets.cfn = strgetfn; - pm.unsetfn = NULL; - pm.ct = 0; - pm.env = NULL; - pm.ename = NULL; - pm.old = NULL; - pm.level = 0; + pm.gsu.s = &nullsetscalar_gsu; for (i = 0; i < builtintab->hsize; i++) for (hn = builtintab->nodes[i]; hn; hn = hn->next) { @@ -680,14 +659,14 @@ getreswords(int dis) /**/ static char ** -reswordsgetfn(Param pm) +reswordsgetfn(UNUSED(Param pm)) { return getreswords(0); } /**/ static char ** -disreswordsgetfn(Param pm) +disreswordsgetfn(UNUSED(Param pm)) { return getreswords(DISABLED); } @@ -711,7 +690,7 @@ setpmoption(Param pm, char *value) /**/ static void -unsetpmoption(Param pm, int exp) +unsetpmoption(Param pm, UNUSED(int exp)) { int n; @@ -723,7 +702,7 @@ unsetpmoption(Param pm, int exp) /**/ static void -setpmoptions(Param pm, HashTable ht) +setpmoptions(UNUSED(Param pm), HashTable ht) { int i; HashNode hn; @@ -736,8 +715,8 @@ setpmoptions(Param pm, HashTable ht) struct value v; char *val; - v.isarr = v.inv = v.a = 0; - v.b = -1; + v.isarr = v.inv = v.start = 0; + v.end = -1; v.arr = NULL; v.pm = (Param) hn; @@ -751,27 +730,30 @@ setpmoptions(Param pm, HashTable ht) deleteparamtable(ht); } +static const struct gsu_scalar pmoption_gsu = +{ strgetfn, setpmoption, unsetpmoption }; + /**/ static HashNode -getpmoption(HashTable ht, char *name) +getpmoption(UNUSED(HashTable ht), char *name) { Param pm = NULL; int n; - pm = (Param) zhalloc(sizeof(struct param)); + pm = (Param) hcalloc(sizeof(struct param)); pm->nam = dupstring(name); pm->flags = PM_SCALAR; - pm->sets.cfn = setpmoption; - pm->gets.cfn = strgetfn; - pm->unsetfn = unsetpmoption; - pm->ct = 0; - pm->env = NULL; - pm->ename = NULL; - pm->old = NULL; - pm->level = 0; + pm->gsu.s = &pmoption_gsu; if ((n = optlookup(name))) - pm->u.str = dupstring(opts[n] ? "on" : "off"); + { + int ison; + if (n > 0) + ison = opts[n]; + else + ison = !opts[-n]; + pm->u.str = dupstring(ison ? "on" : "off"); + } else { pm->u.str = dupstring(""); pm->flags |= PM_UNSET; @@ -781,21 +763,15 @@ getpmoption(HashTable ht, char *name) /**/ static void -scanpmoptions(HashTable ht, ScanFunc func, int flags) +scanpmoptions(UNUSED(HashTable ht), ScanFunc func, int flags) { struct param pm; int i; HashNode hn; + memset((void *)&pm, 0, sizeof(struct param)); pm.flags = PM_SCALAR; - pm.sets.cfn = setpmoption; - pm.gets.cfn = strgetfn; - pm.unsetfn = unsetpmoption; - pm.ct = 0; - pm.env = NULL; - pm.ename = NULL; - pm.old = NULL; - pm.level = 0; + pm.gsu.s = &pmoption_gsu; for (i = 0; i < optiontab->hsize; i++) for (hn = optiontab->nodes[i]; hn; hn = hn->next) { @@ -814,7 +790,7 @@ static int modpmfound; /**/ static void -modpmbuiltinscan(HashNode hn, int dummy) +modpmbuiltinscan(HashNode hn, UNUSED(int dummy)) { if (!(((Builtin) hn)->flags & BINF_ADDED) && !strcmp(((Builtin) hn)->optstr, modpmname)) @@ -823,7 +799,7 @@ modpmbuiltinscan(HashNode hn, int dummy) /**/ static void -modpmparamscan(HashNode hn, int dummy) +modpmparamscan(HashNode hn, UNUSED(int dummy)) { if ((((Param) hn)->flags & PM_AUTOLOAD) && !strcmp(((Param) hn)->u.str, modpmname)) @@ -845,23 +821,16 @@ findmodnode(LinkList l, char *nam) /**/ static HashNode -getpmmodule(HashTable ht, char *name) +getpmmodule(UNUSED(HashTable ht), char *name) { Param pm = NULL; char *type = NULL; LinkNode node; - pm = (Param) zhalloc(sizeof(struct param)); + pm = (Param) hcalloc(sizeof(struct param)); pm->nam = dupstring(name); pm->flags = PM_SCALAR | PM_READONLY; - pm->sets.cfn = NULL; - pm->gets.cfn = strgetfn; - pm->unsetfn = NULL; - pm->ct = 0; - pm->env = NULL; - pm->ename = NULL; - pm->old = NULL; - pm->level = 0; + pm->gsu.s = &nullsetscalar_gsu; if (!type) { Module m; @@ -870,7 +839,8 @@ getpmmodule(HashTable ht, char *name) m = (Module) getdata(node); if (m->u.handle && !(m->flags & MOD_UNLOAD) && !strcmp(name, m->nam)) { - type = "loaded"; + type = ((m->flags & MOD_ALIAS) ? + dyncat("alias:", m->u.alias) : "loaded"); break; } } @@ -904,7 +874,7 @@ getpmmodule(HashTable ht, char *name) /**/ static void -scanpmmodules(HashTable ht, ScanFunc func, int flags) +scanpmmodules(UNUSED(HashTable ht), ScanFunc func, int flags) { struct param pm; int i; @@ -913,23 +883,18 @@ scanpmmodules(HashTable ht, ScanFunc func, int flags) LinkNode node; Module m; Conddef p; + char *loaded = dupstring("loaded"); + memset((void *)&pm, 0, sizeof(struct param)); pm.flags = PM_SCALAR | PM_READONLY; - pm.sets.cfn = NULL; - pm.gets.cfn = strgetfn; - pm.unsetfn = NULL; - pm.ct = 0; - pm.env = NULL; - pm.ename = NULL; - pm.old = NULL; - pm.level = 0; - - pm.u.str = dupstring("builtin"); - pm.u.str = dupstring("loaded"); + pm.gsu.s = &nullsetscalar_gsu; + for (node = firstnode(modules); node; incnode(node)) { m = (Module) getdata(node); if (m->u.handle && !(m->flags & MOD_UNLOAD)) { pm.nam = m->nam; + pm.u.str = ((m->flags & MOD_ALIAS) ? + dyncat("alias:", m->u.alias) : loaded); addlinknode(done, pm.nam); func((HashNode) &pm, flags); } @@ -965,7 +930,7 @@ scanpmmodules(HashTable ht, ScanFunc func, int flags) /**/ static void -dirssetfn(Param pm, char **x) +dirssetfn(UNUSED(Param pm), char **x) { char **ox = x; @@ -981,7 +946,7 @@ dirssetfn(Param pm, char **x) /**/ static char ** -dirsgetfn(Param pm) +dirsgetfn(UNUSED(Param pm)) { int l = countlinknodes(dirstack); char **ret = (char **) zhalloc((l + 1) * sizeof(char *)), **p; @@ -998,23 +963,28 @@ dirsgetfn(Param pm) /**/ static HashNode -getpmhistory(HashTable ht, char *name) +getpmhistory(UNUSED(HashTable ht), char *name) { Param pm = NULL; Histent he; + char *p; + int ok = 1; - pm = (Param) zhalloc(sizeof(struct param)); + pm = (Param) hcalloc(sizeof(struct param)); pm->nam = dupstring(name); pm->flags = PM_SCALAR | PM_READONLY; - pm->sets.cfn = NULL; - pm->gets.cfn = strgetfn; - pm->unsetfn = NULL; - pm->ct = 0; - pm->env = NULL; - pm->ename = NULL; - pm->old = NULL; - pm->level = 0; - if ((he = quietgethist(atoi(name)))) + pm->gsu.s = &nullsetscalar_gsu; + + if (*name != '0' || name[1]) { + if (*name == '0') + ok = 0; + else { + for (p = name; *p && idigit(*p); p++); + if (*p) + ok = 0; + } + } + if (ok && (he = quietgethist(atoi(name)))) pm->u.str = dupstring(he->text); else { pm->u.str = dupstring(""); @@ -1025,26 +995,20 @@ getpmhistory(HashTable ht, char *name) /**/ static void -scanpmhistory(HashTable ht, ScanFunc func, int flags) +scanpmhistory(UNUSED(HashTable ht), ScanFunc func, int flags) { struct param pm; int i = addhistnum(curhist, -1, HIST_FOREIGN); - Histent he = quietgethistent(i, GETHIST_UPWARD); + Histent he = gethistent(i, GETHIST_UPWARD); char buf[40]; + memset((void *)&pm, 0, sizeof(struct param)); pm.flags = PM_SCALAR | PM_READONLY; - pm.sets.cfn = NULL; - pm.gets.cfn = strgetfn; - pm.unsetfn = NULL; - pm.ct = 0; - pm.env = NULL; - pm.ename = NULL; - pm.old = NULL; - pm.level = 0; + pm.gsu.s = &nullsetscalar_gsu; while (he) { if (func != scancountparams) { - sprintf(buf, "%d", he->histnum); + convbase(buf, he->histnum, 10); pm.nam = dupstring(buf); if ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) || !(flags & SCANPM_WANTKEYS)) @@ -1060,13 +1024,17 @@ scanpmhistory(HashTable ht, ScanFunc func, int flags) /**/ static char ** -histwgetfn(Param pm) +histwgetfn(UNUSED(Param pm)) { char **ret, **p, *h, *e, sav; - LinkList l = newlinklist(); + LinkList l = newlinklist(), ll; LinkNode n; int i = addhistnum(curhist, -1, HIST_FOREIGN), iw; - Histent he = quietgethistent(i, GETHIST_UPWARD); + Histent he = gethistent(i, GETHIST_UPWARD); + + if ((ll = bufferwords(NULL, NULL, NULL))) + for (n = firstnode(ll); n; incnode(n)) + pushnode(l, getdata(n)); while (he) { for (iw = he->nwords - 1; iw >= 0; iw--) { @@ -1114,24 +1082,17 @@ pmjobtext(int job) /**/ static HashNode -getpmjobtext(HashTable ht, char *name) +getpmjobtext(UNUSED(HashTable ht), char *name) { Param pm = NULL; int job; - pm = (Param) zhalloc(sizeof(struct param)); + pm = (Param) hcalloc(sizeof(struct param)); pm->nam = dupstring(name); pm->flags = PM_SCALAR | PM_READONLY; - pm->sets.cfn = NULL; - pm->gets.cfn = strgetfn; - pm->unsetfn = NULL; - pm->ct = 0; - pm->env = NULL; - pm->ename = NULL; - pm->old = NULL; - pm->level = 0; - - if ((job = atoi(name)) >= 1 && job < MAXJOB && + pm->gsu.s = &nullsetscalar_gsu; + + if ((job = atoi(name)) >= 1 && job <= maxjob && jobtab[job].stat && jobtab[job].procs && !(jobtab[job].stat & STAT_NOPRINT)) pm->u.str = pmjobtext(job); @@ -1144,23 +1105,17 @@ getpmjobtext(HashTable ht, char *name) /**/ static void -scanpmjobtexts(HashTable ht, ScanFunc func, int flags) +scanpmjobtexts(UNUSED(HashTable ht), ScanFunc func, int flags) { struct param pm; int job; char buf[40]; + memset((void *)&pm, 0, sizeof(struct param)); pm.flags = PM_SCALAR | PM_READONLY; - pm.sets.cfn = NULL; - pm.gets.cfn = strgetfn; - pm.unsetfn = NULL; - pm.ct = 0; - pm.env = NULL; - pm.ename = NULL; - pm.old = NULL; - pm.level = 0; - - for (job = 1; job < MAXJOB; job++) { + pm.gsu.s = &nullsetscalar_gsu; + + for (job = 1; job <= maxjob; job++) { if (jobtab[job].stat && jobtab[job].procs && !(jobtab[job].stat & STAT_NOPRINT)) { if (func != scancountparams) { @@ -1182,14 +1137,21 @@ static char * pmjobstate(int job) { Process pn; - char buf[256], buf2[128], *ret, *state; + char buf[256], buf2[128], *ret, *state, *cp; + + if (job == curjob) + cp = ":+"; + else if (job == prevjob) + cp = ":-"; + else + cp = ":"; if (jobtab[job].stat & STAT_DONE) - ret = dupstring("done"); + ret = dyncat("done", cp); else if (jobtab[job].stat & STAT_STOPPED) - ret = dupstring("suspended"); + ret = dyncat("suspended", cp); else - ret = dupstring("running"); + ret = dyncat("running", cp); for (pn = jobtab[job].procs; pn; pn = pn->next) { @@ -1208,7 +1170,7 @@ pmjobstate(int job) else state = sigmsg(WTERMSIG(pn->status)); - sprintf(buf, ":%d=%s", pn->pid, state); + sprintf(buf, ":%d=%s", (int)pn->pid, state); ret = dyncat(ret, buf); } @@ -1217,24 +1179,17 @@ pmjobstate(int job) /**/ static HashNode -getpmjobstate(HashTable ht, char *name) +getpmjobstate(UNUSED(HashTable ht), char *name) { Param pm = NULL; int job; - pm = (Param) zhalloc(sizeof(struct param)); + pm = (Param) hcalloc(sizeof(struct param)); pm->nam = dupstring(name); pm->flags = PM_SCALAR | PM_READONLY; - pm->sets.cfn = NULL; - pm->gets.cfn = strgetfn; - pm->unsetfn = NULL; - pm->ct = 0; - pm->env = NULL; - pm->ename = NULL; - pm->old = NULL; - pm->level = 0; - - if ((job = atoi(name)) >= 1 && job < MAXJOB && + pm->gsu.s = &nullsetscalar_gsu; + + if ((job = atoi(name)) >= 1 && job <= maxjob && jobtab[job].stat && jobtab[job].procs && !(jobtab[job].stat & STAT_NOPRINT)) pm->u.str = pmjobstate(job); @@ -1247,23 +1202,17 @@ getpmjobstate(HashTable ht, char *name) /**/ static void -scanpmjobstates(HashTable ht, ScanFunc func, int flags) +scanpmjobstates(UNUSED(HashTable ht), ScanFunc func, int flags) { struct param pm; int job; char buf[40]; + memset((void *)&pm, 0, sizeof(struct param)); pm.flags = PM_SCALAR | PM_READONLY; - pm.sets.cfn = NULL; - pm.gets.cfn = strgetfn; - pm.unsetfn = NULL; - pm.ct = 0; - pm.env = NULL; - pm.ename = NULL; - pm.old = NULL; - pm.level = 0; - - for (job = 1; job < MAXJOB; job++) { + pm.gsu.s = &nullsetscalar_gsu; + + for (job = 1; job <= maxjob; job++) { if (jobtab[job].stat && jobtab[job].procs && !(jobtab[job].stat & STAT_NOPRINT)) { if (func != scancountparams) { @@ -1292,24 +1241,17 @@ pmjobdir(int job) /**/ static HashNode -getpmjobdir(HashTable ht, char *name) +getpmjobdir(UNUSED(HashTable ht), char *name) { Param pm = NULL; int job; - pm = (Param) zhalloc(sizeof(struct param)); + pm = (Param) hcalloc(sizeof(struct param)); pm->nam = dupstring(name); pm->flags = PM_SCALAR | PM_READONLY; - pm->sets.cfn = NULL; - pm->gets.cfn = strgetfn; - pm->unsetfn = NULL; - pm->ct = 0; - pm->env = NULL; - pm->ename = NULL; - pm->old = NULL; - pm->level = 0; - - if ((job = atoi(name)) >= 1 && job < MAXJOB && + pm->gsu.s = &nullsetscalar_gsu; + + if ((job = atoi(name)) >= 1 && job <= maxjob && jobtab[job].stat && jobtab[job].procs && !(jobtab[job].stat & STAT_NOPRINT)) pm->u.str = pmjobdir(job); @@ -1322,23 +1264,17 @@ getpmjobdir(HashTable ht, char *name) /**/ static void -scanpmjobdirs(HashTable ht, ScanFunc func, int flags) +scanpmjobdirs(UNUSED(HashTable ht), ScanFunc func, int flags) { struct param pm; int job; char buf[40]; + memset((void *)&pm, 0, sizeof(struct param)); pm.flags = PM_SCALAR | PM_READONLY; - pm.sets.cfn = NULL; - pm.gets.cfn = strgetfn; - pm.unsetfn = NULL; - pm.ct = 0; - pm.env = NULL; - pm.ename = NULL; - pm.old = NULL; - pm.level = 0; - - for (job = 1; job < MAXJOB; job++) { + pm.gsu.s = &nullsetscalar_gsu; + + for (job = 1; job <= maxjob; job++) { if (jobtab[job].stat && jobtab[job].procs && !(jobtab[job].stat & STAT_NOPRINT)) { if (func != scancountparams) { @@ -1359,16 +1295,20 @@ scanpmjobdirs(HashTable ht, ScanFunc func, int flags) static void setpmnameddir(Param pm, char *value) { - if (!value || *value != '/' || strlen(value) >= PATH_MAX) - zwarn("invalid value: %s", value, 0); - else - adduserdir(pm->nam, value, 0, 1); - zsfree(value); + if (!value) + zwarn("invalid value: ''", NULL, 0); + else { + Nameddir nd = (Nameddir) zshcalloc(sizeof(*nd)); + + nd->flags = 0; + nd->dir = value; + nameddirtab->addnode(nameddirtab, ztrdup(pm->nam), nd); + } } /**/ static void -unsetpmnameddir(Param pm, int exp) +unsetpmnameddir(Param pm, UNUSED(int exp)) { HashNode hd = nameddirtab->removenode(nameddirtab, pm->nam); @@ -1378,7 +1318,7 @@ unsetpmnameddir(Param pm, int exp) /**/ static void -setpmnameddirs(Param pm, HashTable ht) +setpmnameddirs(UNUSED(Param pm), HashTable ht) { int i; HashNode hn, next, hd; @@ -1399,16 +1339,20 @@ setpmnameddirs(Param pm, HashTable ht) struct value v; char *val; - v.isarr = v.inv = v.a = 0; - v.b = -1; + v.isarr = v.inv = v.start = 0; + v.end = -1; v.arr = NULL; v.pm = (Param) hn; - if (!(val = getstrvalue(&v)) || *val != '/' || - strlen(val) >= PATH_MAX) - zwarn("invalid value: %s", val, 0); - else - adduserdir(hn->nam, val, 0, 1); + if (!(val = getstrvalue(&v))) + zwarn("invalid value: ''", NULL, 0); + else { + Nameddir nd = (Nameddir) zshcalloc(sizeof(*nd)); + + nd->flags = 0; + nd->dir = ztrdup(val); + nameddirtab->addnode(nameddirtab, ztrdup(hn->nam), nd); + } } /* The INTERACTIVE stuff ensures that the dirs are not immediatly removed @@ -1420,24 +1364,20 @@ setpmnameddirs(Param pm, HashTable ht) opts[INTERACTIVE] = i; } +static const struct gsu_scalar pmnamedir_gsu = +{ strgetfn, setpmnameddir, unsetpmnameddir }; + /**/ static HashNode -getpmnameddir(HashTable ht, char *name) +getpmnameddir(UNUSED(HashTable ht), char *name) { Param pm = NULL; Nameddir nd; - pm = (Param) zhalloc(sizeof(struct param)); + pm = (Param) hcalloc(sizeof(struct param)); pm->nam = dupstring(name); pm->flags = PM_SCALAR; - pm->sets.cfn = setpmnameddir; - pm->gets.cfn = strgetfn; - pm->unsetfn = unsetpmnameddir; - pm->ct = 0; - pm->env = NULL; - pm->ename = NULL; - pm->old = NULL; - pm->level = 0; + pm->gsu.s = &pmnamedir_gsu; if ((nd = (Nameddir) nameddirtab->getnode(nameddirtab, name)) && !(nd->flags & ND_USERNAME)) pm->u.str = dupstring(nd->dir); @@ -1450,22 +1390,16 @@ getpmnameddir(HashTable ht, char *name) /**/ static void -scanpmnameddirs(HashTable ht, ScanFunc func, int flags) +scanpmnameddirs(UNUSED(HashTable ht), ScanFunc func, int flags) { struct param pm; int i; HashNode hn; Nameddir nd; + memset((void *)&pm, 0, sizeof(struct param)); pm.flags = PM_SCALAR; - pm.sets.cfn = setpmnameddir; - pm.gets.cfn = strgetfn; - pm.unsetfn = unsetpmnameddir; - pm.ct = 0; - pm.env = NULL; - pm.ename = NULL; - pm.old = NULL; - pm.level = 0; + pm.gsu.s = &pmnamedir_gsu; for (i = 0; i < nameddirtab->hsize; i++) for (hn = nameddirtab->nodes[i]; hn; hn = hn->next) { @@ -1484,24 +1418,17 @@ scanpmnameddirs(HashTable ht, ScanFunc func, int flags) /**/ static HashNode -getpmuserdir(HashTable ht, char *name) +getpmuserdir(UNUSED(HashTable ht), char *name) { Param pm = NULL; Nameddir nd; nameddirtab->filltable(nameddirtab); - pm = (Param) zhalloc(sizeof(struct param)); + pm = (Param) hcalloc(sizeof(struct param)); pm->nam = dupstring(name); pm->flags = PM_SCALAR | PM_READONLY; - pm->sets.cfn = NULL; - pm->gets.cfn = strgetfn; - pm->unsetfn = NULL; - pm->ct = 0; - pm->env = NULL; - pm->ename = NULL; - pm->old = NULL; - pm->level = 0; + pm->gsu.s = &nullsetscalar_gsu; if ((nd = (Nameddir) nameddirtab->getnode(nameddirtab, name)) && (nd->flags & ND_USERNAME)) pm->u.str = dupstring(nd->dir); @@ -1514,7 +1441,7 @@ getpmuserdir(HashTable ht, char *name) /**/ static void -scanpmuserdirs(HashTable ht, ScanFunc func, int flags) +scanpmuserdirs(UNUSED(HashTable ht), ScanFunc func, int flags) { struct param pm; int i; @@ -1523,15 +1450,9 @@ scanpmuserdirs(HashTable ht, ScanFunc func, int flags) nameddirtab->filltable(nameddirtab); + memset((void *)&pm, 0, sizeof(struct param)); pm.flags = PM_SCALAR | PM_READONLY; - pm.sets.cfn = NULL; - pm.gets.cfn = strgetfn; - pm.unsetfn = NULL; - pm.ct = 0; - pm.env = NULL; - pm.ename = NULL; - pm.old = NULL; - pm.level = 0; + pm.gsu.s = &nullsetscalar_gsu; for (i = 0; i < nameddirtab->hsize; i++) for (hn = nameddirtab->nodes[i]; hn; hn = hn->next) { @@ -1546,54 +1467,61 @@ scanpmuserdirs(HashTable ht, ScanFunc func, int flags) } } -/* Functions for the regularaliases and globalaliases special parameters. */ +/* Functions for the raliases, galiases and saliases special parameters. */ /**/ static void -setralias(Param pm, char *value, int dis) +setalias(HashTable ht, Param pm, char *value, int flags) { - aliastab->addnode(aliastab, ztrdup(pm->nam), createaliasnode(value, dis)); + ht->addnode(ht, ztrdup(pm->nam), + createaliasnode(value, flags)); } /**/ static void setpmralias(Param pm, char *value) { - setralias(pm, value, 0); + setalias(aliastab, pm, value, 0); } /**/ static void setpmdisralias(Param pm, char *value) { - setralias(pm, value, DISABLED); + setalias(aliastab, pm, value, DISABLED); } /**/ static void -setgalias(Param pm, char *value, int dis) +setpmgalias(Param pm, char *value) { - aliastab->addnode(aliastab, ztrdup(pm->nam), - createaliasnode(value, dis | ALIAS_GLOBAL)); + setalias(aliastab, pm, value, ALIAS_GLOBAL); } /**/ static void -setpmgalias(Param pm, char *value) +setpmdisgalias(Param pm, char *value) { - setgalias(pm, value, 0); + setalias(aliastab, pm, value, ALIAS_GLOBAL|DISABLED); } /**/ static void -setpmdisgalias(Param pm, char *value) +setpmsalias(Param pm, char *value) { - setgalias(pm, value, DISABLED); + setalias(sufaliastab, pm, value, ALIAS_SUFFIX); } /**/ static void -unsetpmalias(Param pm, int exp) +setpmdissalias(Param pm, char *value) +{ + setalias(sufaliastab, pm, value, ALIAS_SUFFIX|DISABLED); +} + +/**/ +static void +unsetpmalias(Param pm, UNUSED(int exp)) { HashNode hd = aliastab->removenode(aliastab, pm->nam); @@ -1603,7 +1531,17 @@ unsetpmalias(Param pm, int exp) /**/ static void -setaliases(Param pm, HashTable ht, int global, int dis) +unsetpmsalias(Param pm, UNUSED(int exp)) +{ + HashNode hd = sufaliastab->removenode(sufaliastab, pm->nam); + + if (hd) + sufaliastab->freenode(hd); +} + +/**/ +static void +setaliases(HashTable alht, UNUSED(Param pm), HashTable ht, int flags) { int i; HashNode hn, next, hd; @@ -1611,13 +1549,18 @@ setaliases(Param pm, HashTable ht, int global, int dis) if (!ht) return; - for (i = 0; i < aliastab->hsize; i++) - for (hn = aliastab->nodes[i]; hn; hn = next) { + for (i = 0; i < alht->hsize; i++) + for (hn = alht->nodes[i]; hn; hn = next) { next = hn->next; - if (((global && (((Alias) hn)->flags & ALIAS_GLOBAL)) || - (!global && !(((Alias) hn)->flags & ALIAS_GLOBAL))) && - (hd = aliastab->removenode(aliastab, hn->nam))) - aliastab->freenode(hd); + /* + * The following respects the DISABLED flag, e.g. + * we get a different behaviour for raliases and dis_raliases. + * The predecessor to this code didn't do that; presumably + * that was a bug. + */ + if (flags == ((Alias)hn)->flags && + (hd = alht->removenode(alht, hn->nam))) + alht->freenode(hd); } for (i = 0; i < ht->hsize; i++) @@ -1625,16 +1568,14 @@ setaliases(Param pm, HashTable ht, int global, int dis) struct value v; char *val; - v.isarr = v.inv = v.a = 0; - v.b = -1; + v.isarr = v.inv = v.start = 0; + v.end = -1; v.arr = NULL; v.pm = (Param) hn; if ((val = getstrvalue(&v))) - aliastab->addnode(aliastab, ztrdup(hn->nam), - createaliasnode(ztrdup(val), - (global ? ALIAS_GLOBAL : 0) | - (dis ? DISABLED : 0))); + alht->addnode(alht, ztrdup(hn->nam), + createaliasnode(ztrdup(val), flags)); } deleteparamtable(ht); } @@ -1643,53 +1584,105 @@ setaliases(Param pm, HashTable ht, int global, int dis) static void setpmraliases(Param pm, HashTable ht) { - setaliases(pm, ht, 0, 0); + setaliases(aliastab, pm, ht, 0); } /**/ static void setpmdisraliases(Param pm, HashTable ht) { - setaliases(pm, ht, 0, DISABLED); + setaliases(aliastab, pm, ht, DISABLED); } /**/ static void setpmgaliases(Param pm, HashTable ht) { - setaliases(pm, ht, 1, 0); + setaliases(aliastab, pm, ht, ALIAS_GLOBAL); } /**/ static void setpmdisgaliases(Param pm, HashTable ht) { - setaliases(pm, ht, 1, DISABLED); + setaliases(aliastab, pm, ht, ALIAS_GLOBAL|DISABLED); +} + +/**/ +static void +setpmsaliases(Param pm, HashTable ht) +{ + setaliases(sufaliastab, pm, ht, ALIAS_SUFFIX); +} + +/**/ +static void +setpmdissaliases(Param pm, HashTable ht) +{ + setaliases(sufaliastab, pm, ht, ALIAS_SUFFIX|DISABLED); +} + +static const struct gsu_scalar pmralias_gsu = +{ strgetfn, setpmralias, unsetpmalias }; +static const struct gsu_scalar pmgalias_gsu = +{ strgetfn, setpmgalias, unsetpmalias }; +static const struct gsu_scalar pmsalias_gsu = +{ strgetfn, setpmsalias, unsetpmsalias }; +static const struct gsu_scalar pmdisralias_gsu = +{ strgetfn, setpmdisralias, unsetpmalias }; +static const struct gsu_scalar pmdisgalias_gsu = +{ strgetfn, setpmdisgalias, unsetpmalias }; +static const struct gsu_scalar pmdissalias_gsu = +{ strgetfn, setpmdissalias, unsetpmsalias }; + +/**/ +static void +assignaliasdefs(Param pm, int flags) +{ + pm->flags = PM_SCALAR; + + /* we really need to squirrel the flags away somewhere... */ + switch (flags) { + case 0: + pm->gsu.s = &pmralias_gsu; + break; + + case ALIAS_GLOBAL: + pm->gsu.s = &pmgalias_gsu; + break; + + case ALIAS_SUFFIX: + pm->gsu.s = &pmsalias_gsu; + break; + + case DISABLED: + pm->gsu.s = &pmdisralias_gsu; + break; + + case ALIAS_GLOBAL|DISABLED: + pm->gsu.s = &pmdisgalias_gsu; + break; + + case ALIAS_SUFFIX|DISABLED: + pm->gsu.s = &pmdissalias_gsu; + break; + } } /**/ static HashNode -getalias(HashTable ht, char *name, int global, int dis) +getalias(HashTable alht, UNUSED(HashTable ht), char *name, int flags) { Param pm = NULL; Alias al; - pm = (Param) zhalloc(sizeof(struct param)); + pm = (Param) hcalloc(sizeof(struct param)); pm->nam = dupstring(name); - pm->flags = PM_SCALAR; - pm->sets.cfn = (global ? (dis ? setpmdisgalias : setpmgalias) : - (dis ? setpmdisralias : setpmralias)); - pm->gets.cfn = strgetfn; - pm->unsetfn = unsetpmalias; - pm->ct = 0; - pm->env = NULL; - pm->ename = NULL; - pm->old = NULL; - pm->level = 0; - if ((al = (Alias) aliastab->getnode2(aliastab, name)) && - ((global && (al->flags & ALIAS_GLOBAL)) || - (!global && !(al->flags & ALIAS_GLOBAL))) && - (dis ? (al->flags & DISABLED) : !(al->flags & DISABLED))) + + assignaliasdefs(pm, flags); + + if ((al = (Alias) alht->getnode2(alht, name)) && + flags == al->flags) pm->u.str = dupstring(al->text); else { pm->u.str = dupstring(""); @@ -1702,61 +1695,65 @@ getalias(HashTable ht, char *name, int global, int dis) static HashNode getpmralias(HashTable ht, char *name) { - return getalias(ht, name, 0, 0); + return getalias(aliastab, ht, name, 0); } /**/ static HashNode getpmdisralias(HashTable ht, char *name) { - return getalias(ht, name, 0, 0); + return getalias(aliastab, ht, name, DISABLED); } /**/ static HashNode getpmgalias(HashTable ht, char *name) { - return getalias(ht, name, 1, 0); + return getalias(aliastab, ht, name, ALIAS_GLOBAL); } /**/ static HashNode getpmdisgalias(HashTable ht, char *name) { - return getalias(ht, name, 1, DISABLED); + return getalias(aliastab, ht, name, ALIAS_GLOBAL|DISABLED); +} + +/**/ +static HashNode +getpmsalias(HashTable ht, char *name) +{ + return getalias(sufaliastab, ht, name, ALIAS_SUFFIX); +} + +/**/ +static HashNode +getpmdissalias(HashTable ht, char *name) +{ + return getalias(sufaliastab, ht, name, ALIAS_SUFFIX|DISABLED); } /**/ static void -scanaliases(HashTable ht, ScanFunc func, int flags, int global, int dis) +scanaliases(HashTable alht, UNUSED(HashTable ht), ScanFunc func, + int pmflags, int alflags) { struct param pm; int i; - HashNode hn; Alias al; - pm.flags = PM_SCALAR; - pm.sets.cfn = (global ? (dis ? setpmdisgalias : setpmgalias) : - (dis ? setpmdisralias : setpmralias)); - pm.gets.cfn = strgetfn; - pm.unsetfn = unsetpmalias; - pm.ct = 0; - pm.env = NULL; - pm.ename = NULL; - pm.old = NULL; - pm.level = 0; - - for (i = 0; i < aliastab->hsize; i++) - for (hn = aliastab->nodes[i]; hn; hn = hn->next) { - if (((global && ((al = (Alias) hn)->flags & ALIAS_GLOBAL)) || - (!global && !((al = (Alias) hn)->flags & ALIAS_GLOBAL))) && - (dis ? (al->flags & DISABLED) : !(al->flags & DISABLED))) { - pm.nam = hn->nam; + memset((void *)&pm, 0, sizeof(struct param)); + assignaliasdefs(&pm, alflags); + + for (i = 0; i < alht->hsize; i++) + for (al = (Alias) alht->nodes[i]; al; al = (Alias) al->next) { + if (alflags == al->flags) { + pm.nam = al->nam; if (func != scancountparams && - ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) || - !(flags & SCANPM_WANTKEYS))) + ((pmflags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) || + !(pmflags & SCANPM_WANTKEYS))) pm.u.str = dupstring(al->text); - func((HashNode) &pm, flags); + func((HashNode) &pm, pmflags); } } } @@ -1765,28 +1762,42 @@ scanaliases(HashTable ht, ScanFunc func, int flags, int global, int dis) static void scanpmraliases(HashTable ht, ScanFunc func, int flags) { - scanaliases(ht, func, flags, 0, 0); + scanaliases(aliastab, ht, func, flags, 0); } /**/ static void scanpmdisraliases(HashTable ht, ScanFunc func, int flags) { - scanaliases(ht, func, flags, 0, DISABLED); + scanaliases(aliastab, ht, func, flags, DISABLED); } /**/ static void scanpmgaliases(HashTable ht, ScanFunc func, int flags) { - scanaliases(ht, func, flags, 1, 0); + scanaliases(aliastab, ht, func, flags, ALIAS_GLOBAL); } /**/ static void scanpmdisgaliases(HashTable ht, ScanFunc func, int flags) { - scanaliases(ht, func, flags, 1, DISABLED); + scanaliases(aliastab, ht, func, flags, ALIAS_GLOBAL|DISABLED); +} + +/**/ +static void +scanpmsaliases(HashTable ht, ScanFunc func, int flags) +{ + scanaliases(sufaliastab, ht, func, flags, ALIAS_SUFFIX); +} + +/**/ +static void +scanpmdissaliases(HashTable ht, ScanFunc func, int flags) +{ + scanaliases(sufaliastab, ht, func, flags, ALIAS_SUFFIX|DISABLED); } /* Table for defined parameters. */ @@ -1796,89 +1807,134 @@ struct pardef { int flags; GetNodeFunc getnfn; ScanTabFunc scantfn; - void (*hsetfn) _((Param, HashTable)); - void (*setfn) _((Param, char **)); - char **(*getfn) _((Param)); - void (*unsetfn) _((Param, int)); + GsuHash hash_gsu; + GsuArray array_gsu; Param pm; }; +/* + * This is a duplicate of nullsethash_gsu. On some systems + * (such as Cygwin) we can't put a pointer to an imported variable + * in a compile-time initialiser, so we use this instead. + */ +static const struct gsu_hash pmnullsethash_gsu = +{ hashgetfn, nullsethashfn, nullunsetfn }; +static const struct gsu_hash pmcommands_gsu = +{ hashgetfn, setpmcommands, stdunsetfn }; +static const struct gsu_hash pmfunctions_gsu = +{ hashgetfn, setpmfunctions, stdunsetfn }; +static const struct gsu_hash pmdisfunctions_gsu = +{ hashgetfn, setpmdisfunctions, stdunsetfn }; +static const struct gsu_hash pmoptions_gsu = +{ hashgetfn, setpmoptions, stdunsetfn }; +static const struct gsu_hash pmnameddirs_gsu = +{ hashgetfn, setpmnameddirs, stdunsetfn }; +static const struct gsu_hash pmraliases_gsu = +{ hashgetfn, setpmraliases, stdunsetfn }; +static const struct gsu_hash pmgaliases_gsu = +{ hashgetfn, setpmgaliases, stdunsetfn }; +static const struct gsu_hash pmsaliases_gsu = +{ hashgetfn, setpmsaliases, stdunsetfn }; +static const struct gsu_hash pmdisraliases_gsu = +{ hashgetfn, setpmdisraliases, stdunsetfn }; +static const struct gsu_hash pmdisgaliases_gsu = +{ hashgetfn, setpmdisgaliases, stdunsetfn }; +static const struct gsu_hash pmdissaliases_gsu = +{ hashgetfn, setpmdissaliases, stdunsetfn }; + +static const struct gsu_array funcstack_gsu = +{ funcstackgetfn, arrsetfn, stdunsetfn }; +static const struct gsu_array reswords_gsu = +{ reswordsgetfn, arrsetfn, stdunsetfn }; +static const struct gsu_array disreswords_gsu = +{ disreswordsgetfn, arrsetfn, stdunsetfn }; +static const struct gsu_array dirs_gsu = +{ dirsgetfn, dirssetfn, stdunsetfn }; +static const struct gsu_array historywords_gsu = +{ histwgetfn, arrsetfn, stdunsetfn }; + static struct pardef partab[] = { { "parameters", PM_READONLY, - getpmparameter, scanpmparameters, hashsetfn, - NULL, NULL, stdunsetfn, NULL }, + getpmparameter, scanpmparameters, &pmnullsethash_gsu, + NULL, NULL }, { "commands", 0, - getpmcommand, scanpmcommands, setpmcommands, - NULL, NULL, stdunsetfn, NULL }, + getpmcommand, scanpmcommands, &pmcommands_gsu, + NULL, NULL }, { "functions", 0, - getpmfunction, scanpmfunctions, setpmfunctions, - NULL, NULL, stdunsetfn, NULL }, + getpmfunction, scanpmfunctions, &pmfunctions_gsu, + NULL, NULL }, { "dis_functions", 0, - getpmdisfunction, scanpmdisfunctions, setpmdisfunctions, - NULL, NULL, stdunsetfn, NULL }, + getpmdisfunction, scanpmdisfunctions, &pmdisfunctions_gsu, + NULL, NULL }, { "funcstack", PM_ARRAY|PM_SPECIAL|PM_READONLY, NULL, NULL, NULL, - arrsetfn, funcstackgetfn, stdunsetfn, NULL }, + &funcstack_gsu, NULL }, { "builtins", PM_READONLY, - getpmbuiltin, scanpmbuiltins, hashsetfn, - NULL, NULL, stdunsetfn, NULL }, + getpmbuiltin, scanpmbuiltins, NULL, + NULL, NULL }, { "dis_builtins", PM_READONLY, - getpmdisbuiltin, scanpmdisbuiltins, hashsetfn, - NULL, NULL, stdunsetfn, NULL }, + getpmdisbuiltin, scanpmdisbuiltins, + NULL, NULL, }, { "reswords", PM_ARRAY|PM_SPECIAL|PM_READONLY, NULL, NULL, NULL, - arrsetfn, reswordsgetfn, stdunsetfn, NULL }, + &reswords_gsu, NULL }, { "dis_reswords", PM_ARRAY|PM_SPECIAL|PM_READONLY, NULL, NULL, NULL, - arrsetfn, disreswordsgetfn, stdunsetfn, NULL }, + &disreswords_gsu, NULL }, { "options", 0, - getpmoption, scanpmoptions, setpmoptions, - NULL, NULL, stdunsetfn, NULL }, + getpmoption, scanpmoptions, &pmoptions_gsu, + NULL, NULL }, { "modules", PM_READONLY, - getpmmodule, scanpmmodules, hashsetfn, - NULL, NULL, stdunsetfn, NULL }, + getpmmodule, scanpmmodules, NULL, + NULL, NULL }, { "dirstack", PM_ARRAY|PM_SPECIAL|PM_REMOVABLE, NULL, NULL, NULL, - dirssetfn, dirsgetfn, stdunsetfn, NULL }, + &dirs_gsu, NULL }, { "history", PM_READONLY, - getpmhistory, scanpmhistory, hashsetfn, - NULL, NULL, stdunsetfn, NULL }, + getpmhistory, scanpmhistory, NULL, + NULL, NULL, }, { "historywords", PM_ARRAY|PM_SPECIAL|PM_READONLY, NULL, NULL, NULL, - arrsetfn, histwgetfn, stdunsetfn, NULL }, + &historywords_gsu, NULL }, { "jobtexts", PM_READONLY, - getpmjobtext, scanpmjobtexts, hashsetfn, - NULL, NULL, stdunsetfn, NULL }, + getpmjobtext, scanpmjobtexts, NULL, + NULL, NULL }, { "jobstates", PM_READONLY, - getpmjobstate, scanpmjobstates, hashsetfn, - NULL, NULL, stdunsetfn, NULL }, + getpmjobstate, scanpmjobstates, NULL, + NULL, NULL }, { "jobdirs", PM_READONLY, - getpmjobdir, scanpmjobdirs, hashsetfn, - NULL, NULL, stdunsetfn, NULL }, + getpmjobdir, scanpmjobdirs, NULL, + NULL, NULL }, { "nameddirs", 0, - getpmnameddir, scanpmnameddirs, setpmnameddirs, - NULL, NULL, stdunsetfn, NULL }, + getpmnameddir, scanpmnameddirs, &pmnameddirs_gsu, + NULL, NULL }, { "userdirs", PM_READONLY, - getpmuserdir, scanpmuserdirs, hashsetfn, - NULL, NULL, stdunsetfn, NULL }, + getpmuserdir, scanpmuserdirs, NULL, + NULL, NULL }, { "aliases", 0, - getpmralias, scanpmraliases, setpmraliases, - NULL, NULL, stdunsetfn, NULL }, + getpmralias, scanpmraliases, &pmraliases_gsu, + NULL, NULL }, { "galiases", 0, - getpmgalias, scanpmgaliases, setpmgaliases, - NULL, NULL, stdunsetfn, NULL }, + getpmgalias, scanpmgaliases, &pmgaliases_gsu, + NULL, NULL }, + { "saliases", 0, + getpmsalias, scanpmsaliases, &pmsaliases_gsu, + NULL, NULL }, { "dis_aliases", 0, - getpmdisralias, scanpmdisraliases, setpmdisraliases, - NULL, NULL, stdunsetfn, NULL }, + getpmdisralias, scanpmdisraliases, &pmdisraliases_gsu, + NULL, NULL }, { "dis_galiases", 0, - getpmdisgalias, scanpmdisgaliases, setpmdisgaliases, - NULL, NULL, stdunsetfn, NULL }, - { NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL } + getpmdisgalias, scanpmdisgaliases, &pmdisgaliases_gsu, + NULL, NULL }, + { "dis_saliases", 0, + getpmdissalias, scanpmdissaliases, &pmdissaliases_gsu, + NULL, NULL }, + { NULL, 0, NULL, NULL, NULL, NULL, NULL } }; /**/ int -setup_(Module m) +setup_(UNUSED(Module m)) { incleanup = 0; @@ -1887,7 +1943,7 @@ setup_(Module m) /**/ int -boot_(Module m) +boot_(UNUSED(Module m)) { /* Create the special associative arrays. * As an example for autoloaded parameters, this is probably a bad @@ -1904,15 +1960,13 @@ boot_(Module m) def->scantfn))) return 1; def->pm->flags |= def->flags; - if (def->hsetfn) - def->pm->sets.hfn = def->hsetfn; + if (def->hash_gsu) + def->pm->gsu.h = def->hash_gsu; } else { - if (!(def->pm = createparam(def->name, def->flags | PM_HIDE | - PM_REMOVABLE))) + if (!(def->pm = createparam(def->name, def->flags | PM_HIDE| + PM_HIDEVAL | PM_REMOVABLE))) return 1; - def->pm->sets.afn = def->setfn; - def->pm->gets.afn = def->getfn; - def->pm->unsetfn = def->unsetfn; + def->pm->gsu.a = def->array_gsu; } } return 0; @@ -1920,7 +1974,7 @@ boot_(Module m) /**/ int -cleanup_(Module m) +cleanup_(UNUSED(Module m)) { Param pm; struct pardef *def; @@ -1939,7 +1993,7 @@ cleanup_(Module m) /**/ int -finish_(Module m) +finish_(UNUSED(Module m)) { return 0; } diff --git a/Src/glob.c b/Src/glob.c index 5a4add319..5334f70fa 100644 --- a/Src/glob.c +++ b/Src/glob.c @@ -38,7 +38,7 @@ /* flag for CSHNULLGLOB */ -typedef struct gmatch *Gmatch; +typedef struct gmatch *Gmatch; struct gmatch { char *name; @@ -55,11 +55,12 @@ struct gmatch { }; #define GS_NAME 1 -#define GS_SIZE 2 -#define GS_ATIME 4 -#define GS_MTIME 8 -#define GS_CTIME 16 -#define GS_LINKS 32 +#define GS_DEPTH 2 +#define GS_SIZE 4 +#define GS_ATIME 8 +#define GS_MTIME 16 +#define GS_CTIME 32 +#define GS_LINKS 64 #define GS_SHIFT 5 #define GS__SIZE (GS_SIZE << GS_SHIFT) @@ -68,14 +69,14 @@ struct gmatch { #define GS__CTIME (GS_CTIME << GS_SHIFT) #define GS__LINKS (GS_LINKS << GS_SHIFT) -#define GS_DESC 2048 +#define GS_DESC 4096 #define GS_NORMAL (GS_SIZE | GS_ATIME | GS_MTIME | GS_CTIME | GS_LINKS) #define GS_LINKED (GS_NORMAL << GS_SHIFT) /**/ int badcshglob; - + /**/ int pathpos; /* position in pathbuf (needed by pattern code) */ @@ -180,9 +181,9 @@ static struct globdata curglobdata; (N).gd_pathpos = pathpos; \ (N).gd_pathbuf = pathbuf; \ (N).gd_pathbufsz = 0; \ - (N).gd_pathbuf = NULL; \ (N).gd_glob_pre = glob_pre; \ (N).gd_glob_suf = glob_suf; \ + pathbuf = NULL; \ } while (0) #define restore_globstate(N) \ @@ -222,18 +223,19 @@ struct complist { /**/ static void -addpath(char *s) +addpath(char *s, int l) { DPUTS(!pathbuf, "BUG: pathbuf not initialised"); - while (pathpos + (int) strlen(s) + 1 >= pathbufsz) + while (pathpos + l + 1 >= pathbufsz) pathbuf = realloc(pathbuf, pathbufsz *= 2); - while ((pathbuf[pathpos++] = *s++)); - pathbuf[pathpos - 1] = '/'; + while (l--) + pathbuf[pathpos++] = *s++; + pathbuf[pathpos++] = '/'; pathbuf[pathpos] = '\0'; } /* stat the filename s appended to pathbuf. l should be true for lstat, * - * false for stat. If st is NULL, the file is only chechked for existance. * + * false for stat. If st is NULL, the file is only checked for existance. * * s == "" is treated as s == ".". This is necessary since on most systems * * foo/ can be used to reference a non-directory foo. Returns nonzero if * * the file does not exists. */ @@ -278,14 +280,17 @@ insert(char *s, int checked) char *news = s; int statted = 0; + queue_signals(); inserts = NULL; if (gf_listtypes || gf_markdirs) { /* Add the type marker to the end of the filename */ mode_t mode; checked = statted = 1; - if (statfullpath(s, &buf, 1)) + if (statfullpath(s, &buf, 1)) { + unqueue_signals(); return; + } mode = buf.st_mode; if (gf_follow) { if (!S_ISLNK(mode) || statfullpath(s, &buf2, 0)) @@ -306,9 +311,10 @@ insert(char *s, int checked) /* Go through the qualifiers, rejecting the file if appropriate */ struct qual *qo, *qn; - if (!statted && statfullpath(s, &buf, 1)) + if (!statted && statfullpath(s, &buf, 1)) { + unqueue_signals(); return; - + } news = dyncat(pathbuf, news); statted = 1; @@ -329,16 +335,20 @@ insert(char *s, int checked) * vice versa. */ if ((!((qn->func) (news, bp, qn->data, qn->sdata)) ^ qn->sense) & 1) { /* Try next alternative, or return if there are no more */ - if (!(qo = qo->or)) + if (!(qo = qo->or)) { + unqueue_signals(); return; + } qn = qo; continue; } qn = qn->next; } } else if (!checked) { - if (statfullpath(s, NULL, 1)) + if (statfullpath(s, NULL, 1)) { + unqueue_signals(); return; + } statted = 1; news = dyncat(pathbuf, news); } else @@ -346,7 +356,7 @@ insert(char *s, int checked) while (!inserts || (news = dupstring(*inserts++))) { if (colonmod) { - /* Handle the remainder of the qualifer: e.g. (:r:s/foo/bar/). */ + /* Handle the remainder of the qualifier: e.g. (:r:s/foo/bar/). */ s = colonmod; modify(&news, &s); } @@ -388,6 +398,7 @@ insert(char *s, int checked) if (!inserts) break; } + unqueue_signals(); } /* Check to see if str is eligible for filename generation. */ @@ -494,14 +505,17 @@ scanner(Complist q) } } if (add) { - addpath(str); + addpath(str, l); if (!closure || !statfullpath("", NULL, 1)) scanner((q->closure) ? q : q->next); pathbuf[pathpos = oppos] = '\0'; } } - } else + } else { + if (str[l]) + str = dupstrpfx(str, l); insert(str, 0); + } } else { /* Do pattern matching on current path section. */ char *fn = pathbuf[pathbufcwd] ? unmeta(pathbuf + pathbufcwd) : "."; @@ -572,8 +586,7 @@ scanner(Complist q) if (statfullpath(fn, &buf, !q->follow)) { if (errno != ENOENT && errno != EINTR && errno != ENOTDIR && !errflag) { - zerr("%e: %s", fn, errno); - errflag = 0; + zwarn("%e: %s", fn, errno); } continue; } @@ -599,8 +612,9 @@ scanner(Complist q) int oppos = pathpos; for (fn = subdirs; fn < subdirs+subdirlen; ) { - addpath(fn); - fn += strlen(fn) + 1; + int l = strlen(fn); + addpath(fn, l); + fn += l + 1; memcpy((char *)&errsfound, fn, sizeof(int)); fn += sizeof(int); scanner((q->closure) ? q : q->next); /* scan next level */ @@ -676,7 +690,7 @@ parsecomplist(char *instr) /* parse single path component */ if (!(p1 = patcompile(instr, compflags|PAT_FILET, &instr))) return NULL; - /* then do the remaining path compoents */ + /* then do the remaining path components */ if (*instr == '/' || !*instr) { int ef = *instr == '/'; @@ -697,6 +711,9 @@ parsecomplist(char *instr) static Complist parsepat(char *str) { + long assert; + int ignore; + patcompstart(); /* * Check for initial globbing flags, so that they don't form @@ -706,7 +723,7 @@ parsepat(char *str) (isset(KSHGLOB) && *str == '@' && str[1] == Inpar && str[2] == Pound)) { str += (*str == Inpar) ? 2 : 3; - if (!patgetglobflags(&str)) + if (!patgetglobflags(&str, &assert, &ignore)) return NULL; } @@ -802,7 +819,7 @@ qgetmodespec(char **s) } if (how == '=' || how == '-') no |= val & mask; - } else { + } else if (!(end && c == end) && c != ',' && c) { t = 07777; while ((c = *p) == '?' || c == Quest || (c >= '0' && c <= '7')) { @@ -826,7 +843,10 @@ qgetmodespec(char **s) yes |= val; else no |= val; - } + } else { + zerr("invalid mode specification", NULL, 0); + return 0; + } } while (end && c != end); *s = p; @@ -842,7 +862,32 @@ gmatchcmp(Gmatch a, Gmatch b) for (i = gf_nsorts, s = gf_sortlist; i; i--, s++) { switch (*s & ~GS_DESC) { case GS_NAME: - r = notstrcmp(&a->name, &b->name); + if (gf_numsort) + r = nstrpcmp(&b->name, &a->name); + else + r = strpcmp(&b->name, &a->name); + break; + case GS_DEPTH: + { + char *aptr = a->name, *bptr = b->name; + int slasha = 0, slashb = 0; + /* Count slashes. Trailing slashes don't count. */ + while (*aptr && *aptr == *bptr) + aptr++, bptr++; + if (*aptr) + for (; aptr[1]; aptr++) + if (*aptr == '/') { + slasha = 1; + break; + } + if (*bptr) + for (; bptr[1]; bptr++) + if (*bptr == '/') { + slashb = 1; + break; + } + r = slasha - slashb; + } break; case GS_SIZE: r = b->size - a->size; @@ -881,13 +926,42 @@ gmatchcmp(Gmatch a, Gmatch b) return 0; } +/* + * Duplicate a list of qualifiers using the `next' linkage (not the + * `or' linkage). Return the head element and set *last (if last non-NULL) + * to point to the last element of the new list. All allocation is on the + * heap (or off the heap?) + */ +static struct qual *dup_qual_list(struct qual *orig, struct qual **lastp) +{ + struct qual *qfirst = NULL, *qlast = NULL; + + while (orig) { + struct qual *qnew = (struct qual *)zhalloc(sizeof(struct qual)); + *qnew = *orig; + qnew->next = qnew->or = NULL; + + if (!qfirst) + qfirst = qnew; + if (qlast) + qlast->next = qnew; + qlast = qnew; + + orig = orig->next; + } + + if (lastp) + *lastp = qlast; + return qfirst; +} + /* Main entry point to the globbing code for filename globbing. * * np points to a node in the list list which will be expanded * * into a series of nodes. */ /**/ void -glob(LinkList list, LinkNode np, int nountok) +zglob(LinkList list, LinkNode np, int nountok) { struct qual *qo, *qn, *ql; LinkNode node = prevnode(np); @@ -896,9 +970,10 @@ glob(LinkList list, LinkNode np, int nountok) Complist q; /* pattern after parsing */ char *ostr = (char *)getdata(np); /* the pattern before the parser */ /* chops it up */ - int first = 0, last = -1; /* index of first/last match to */ - /* return */ + int first = 0, end = -1; /* index of first match to return */ + /* and index+1 of the last match */ struct globdata saved; /* saved glob state */ + int nobareglob = !isset(BAREGLOBQUAL); if (unset(GLOBOPT) || !haswilds(ostr)) { if (!nountok) @@ -908,13 +983,22 @@ glob(LinkList list, LinkNode np, int nountok) save_globstate(saved); str = dupstring(ostr); - sl = strlen(str); uremnode(list, np); - /* Initialise state variables for current file pattern */ - qo = qn = quals = ql = NULL; + /* quals will hold the complete list of qualifiers (file static). */ + quals = NULL; + /* + * qualct and qualorct indicate we have qualifiers in the last + * alternative, or a set of alternatives, respectively. They + * are not necessarily an accurate count, however. + */ qualct = qualorct = 0; + /* + * colonmod is a concatenated list of all colon modifiers found in + * all sets of qualifiers. + */ colonmod = NULL; + /* The gf_* flags are qualifiers which are applied globally. */ gf_nullglob = isset(NULLGLOB); gf_markdirs = isset(MARKDIRS); gf_listtypes = gf_follow = 0; @@ -923,432 +1007,573 @@ glob(LinkList list, LinkNode np, int nountok) gf_sorts = gf_nsorts = 0; /* Check for qualifiers */ - if (isset(BAREGLOBQUAL) && str[sl - 1] == Outpar) { + while (!nobareglob || isset(EXTENDEDGLOB)) { + struct qual *newquals; char *s; + int sense, paren; + off_t data; + char *sdata, *newcolonmod; + int (*func) _((char *, Statptr, off_t, char *)); + + /* + * Initialise state variables for current file pattern. + * newquals is the root for the linked list of all qualifiers. + * qo is the root of the current list of alternatives. + * ql is the end of the current alternative where the `next' will go. + * qn is the current qualifier node to be added. + * + * Here is an attempt at a diagram. An `or' is added horizontally + * to the top line, a `next' at the bottom of the right hand line. + * `qn' is usually NULL unless a new `or' has just been added. + * + * quals -> x -> x -> qo + * | | | + * x x x + * | | + * x ql + * + * In fact, after each loop the complete set is in the file static + * `quals'. Then, if we have a second set of qualifiers, we merge + * the lists together. This is only tricky if one or both have an + * `or' in them; then we need to distribute over all alternatives. + */ + newquals = qo = qn = ql = NULL; + + sl = strlen(str); + if (str[sl - 1] != Outpar) + break; /* Check these are really qualifiers, not a set of * - * alternatives or exclusions */ - for (s = str + sl - 2; *s != Inpar; s--) - if (*s == Bar || *s == Outpar || - (isset(EXTENDEDGLOB) && *s == Tilde)) + * alternatives or exclusions. We can be more * + * lenient with an explicit (#q) than with a bare * + * set of qualifiers. */ + paren = 0; + for (s = str + sl - 2; *s && (*s != Inpar || paren); s--) { + switch (*s) { + case Outpar: + paren++; /*FALLTHROUGH*/ + case Bar: + nobareglob = 1; + break; + case Tilde: + if (isset(EXTENDEDGLOB)) + nobareglob = 1; + break; + case Inpar: + paren--; + break; + } + } + if (*s != Inpar) + break; + if (isset(EXTENDEDGLOB) && s[1] == Pound) { + if (s[2] == 'q') { + *s = 0; + s += 2; + } else break; - if (*s == Inpar && (!isset(EXTENDEDGLOB) || s[1] != Pound)) { - /* Real qualifiers found. */ - int sense = 0; /* bit 0 for match (0)/don't match (1) */ - /* bit 1 for follow links (2), don't (0) */ - off_t data = 0; /* Any numerical argument required */ - char *sdata = NULL; /* Any list argument required */ - int (*func) _((char *, Statptr, off_t, char *)); - - str[sl-1] = 0; - *s++ = 0; - while (*s && !colonmod) { - func = (int (*) _((char *, Statptr, off_t, char *)))0; - if (idigit(*s)) { - /* Store numeric argument for qualifier */ + } else if (nobareglob) + break; + + /* Real qualifiers found. */ + nobareglob = 1; + sense = 0; /* bit 0 for match (0)/don't match (1) */ + /* bit 1 for follow links (2), don't (0) */ + data = 0; /* Any numerical argument required */ + sdata = NULL; /* Any list argument required */ + newcolonmod = NULL; /* Contains trailing colon modifiers */ + + str[sl-1] = 0; + *s++ = 0; + while (*s && !newcolonmod) { + func = (int (*) _((char *, Statptr, off_t, char *)))0; + if (idigit(*s)) { + /* Store numeric argument for qualifier */ + func = qualflags; + data = 0; + sdata = NULL; + while (idigit(*s)) + data = data * 010 + (*s++ - '0'); + } else if (*s == ',') { + /* A comma separates alternative sets of qualifiers */ + s++; + sense = 0; + if (qualct) { + qn = (struct qual *)hcalloc(sizeof *qn); + qo->or = qn; + qo = qn; + qualorct++; + qualct = 0; + ql = NULL; + } + } else { + switch (*s++) { + case ':': + /* Remaining arguments are history-type * + * colon substitutions, handled separately. */ + newcolonmod = s - 1; + untokenize(newcolonmod); + if (colonmod) { + /* remember we're searching backwards */ + colonmod = dyncat(newcolonmod, colonmod); + } else + colonmod = newcolonmod; + break; + case Hat: + case '^': + /* Toggle sense: go from positive to * + * negative match and vice versa. */ + sense ^= 1; + break; + case '-': + /* Toggle matching of symbolic links */ + sense ^= 2; + break; + case '@': + /* Match symbolic links */ + func = qualislnk; + break; + case Equals: + case '=': + /* Match sockets */ + func = qualissock; + break; + case 'p': + /* Match named pipes */ + func = qualisfifo; + break; + case '/': + /* Match directories */ + func = qualisdir; + break; + case '.': + /* Match regular files */ + func = qualisreg; + break; + case '%': + /* Match special files: block, * + * character or any device */ + if (*s == 'b') + s++, func = qualisblk; + else if (*s == 'c') + s++, func = qualischr; + else + func = qualisdev; + break; + case Star: + /* Match executable plain files */ + func = qualiscom; + break; + case 'R': + /* Match world-readable files */ func = qualflags; - data = 0; - sdata = NULL; - while (idigit(*s)) - data = data * 010 + (*s++ - '0'); - } else if (*s == ',') { - /* A comma separates alternative sets of qualifiers */ - s++; - sense = 0; - if (qualct) { - qn = (struct qual *)hcalloc(sizeof *qn); - qo->or = qn; - qo = qn; - qualorct++; - qualct = 0; - ql = NULL; - } - } else - switch (*s++) { - case ':': - /* Remaining arguments are history-type * - * colon substitutions, handled separately. */ - colonmod = s - 1; - untokenize(colonmod); - break; - case Hat: - case '^': - /* Toggle sense: go from positive to * - * negative match and vice versa. */ - sense ^= 1; - break; - case '-': - /* Toggle matching of symbolic links */ - sense ^= 2; - break; - case '@': - /* Match symbolic links */ - func = qualislnk; - break; - case Equals: - case '=': - /* Match sockets */ - func = qualissock; - break; - case 'p': - /* Match named pipes */ - func = qualisfifo; - break; - case '/': - /* Match directories */ - func = qualisdir; - break; - case '.': - /* Match regular files */ - func = qualisreg; - break; - case '%': - /* Match special files: block, * - * character or any device */ - if (*s == 'b') - s++, func = qualisblk; - else if (*s == 'c') - s++, func = qualischr; - else - func = qualisdev; - break; - case Star: - /* Match executable plain files */ - func = qualiscom; - break; - case 'R': - /* Match world-readable files */ - func = qualflags; - data = 0004; - break; - case 'W': - /* Match world-writeable files */ - func = qualflags; - data = 0002; - break; - case 'X': - /* Match world-executable files */ - func = qualflags; - data = 0001; - break; - case 'A': - func = qualflags; - data = 0040; - break; - case 'I': - func = qualflags; - data = 0020; - break; - case 'E': - func = qualflags; - data = 0010; - break; - case 'r': - /* Match files readable by current process */ - func = qualflags; - data = 0400; - break; - case 'w': - /* Match files writeable by current process */ - func = qualflags; - data = 0200; - break; - case 'x': - /* Match files executable by current process */ - func = qualflags; - data = 0100; - break; - case 's': - /* Match setuid files */ - func = qualflags; - data = 04000; - break; - case 'S': - /* Match setgid files */ - func = qualflags; - data = 02000; - break; - case 't': - func = qualflags; - data = 01000; - break; - case 'd': - /* Match device files by device number * - * (as given by stat's st_dev element). */ - func = qualdev; + data = 0004; + break; + case 'W': + /* Match world-writeable files */ + func = qualflags; + data = 0002; + break; + case 'X': + /* Match world-executable files */ + func = qualflags; + data = 0001; + break; + case 'A': + func = qualflags; + data = 0040; + break; + case 'I': + func = qualflags; + data = 0020; + break; + case 'E': + func = qualflags; + data = 0010; + break; + case 'r': + /* Match files readable by current process */ + func = qualflags; + data = 0400; + break; + case 'w': + /* Match files writeable by current process */ + func = qualflags; + data = 0200; + break; + case 'x': + /* Match files executable by current process */ + func = qualflags; + data = 0100; + break; + case 's': + /* Match setuid files */ + func = qualflags; + data = 04000; + break; + case 'S': + /* Match setgid files */ + func = qualflags; + data = 02000; + break; + case 't': + func = qualflags; + data = 01000; + break; + case 'd': + /* Match device files by device number * + * (as given by stat's st_dev element). */ + func = qualdev; + data = qgetnum(&s); + break; + case 'l': + /* Match files with the given no. of hard links */ + func = qualnlink; + g_amc = -1; + goto getrange; + case 'U': + /* Match files owned by effective user ID */ + func = qualuid; + data = geteuid(); + break; + case 'G': + /* Match files owned by effective group ID */ + func = qualgid; + data = getegid(); + break; + case 'u': + /* Match files owned by given user id */ + func = qualuid; + /* either the actual uid... */ + if (idigit(*s)) data = qgetnum(&s); - break; - case 'l': - /* Match files with the given no. of hard links */ - func = qualnlink; - g_amc = -1; - goto getrange; - case 'U': - /* Match files owned by effective user ID */ - func = qualuid; - data = geteuid(); - break; - case 'G': - /* Match files owned by effective group ID */ - func = qualgid; - data = getegid(); - break; - case 'u': - /* Match files owned by given user id */ - func = qualuid; - /* either the actual uid... */ - if (idigit(*s)) - data = qgetnum(&s); - else { - /* ... or a user name */ - char sav, *tt; - - /* Find matching delimiters */ - tt = get_strarg(s); - if (!*tt) { - zerr("missing end of name", - NULL, 0); - data = 0; - } else { + else { + /* ... or a user name */ + char sav, *tt; + + /* Find matching delimiters */ + tt = get_strarg(s); + if (!*tt) { + zerr("missing end of name", + NULL, 0); + data = 0; + } else { #ifdef HAVE_GETPWNAM - struct passwd *pw; - sav = *tt; - *tt = '\0'; - - if ((pw = getpwnam(s + 1))) - data = pw->pw_uid; - else { - zerr("unknown user", NULL, 0); - data = 0; - } - *tt = sav; -#else /* !HAVE_GETPWNAM */ - sav = *tt; + struct passwd *pw; + sav = *tt; + *tt = '\0'; + + if ((pw = getpwnam(s + 1))) + data = pw->pw_uid; + else { zerr("unknown user", NULL, 0); data = 0; -#endif /* !HAVE_GETPWNAM */ - if (sav) - s = tt + 1; - else - s = tt; } + *tt = sav; +#else /* !HAVE_GETPWNAM */ + sav = *tt; + zerr("unknown user", NULL, 0); + data = 0; +#endif /* !HAVE_GETPWNAM */ + if (sav) + s = tt + 1; + else + s = tt; } - break; - case 'g': - /* Given gid or group id... works like `u' */ - func = qualgid; - /* either the actual gid... */ - if (idigit(*s)) - data = qgetnum(&s); - else { - /* ...or a delimited group name. */ - char sav, *tt; - - tt = get_strarg(s); - if (!*tt) { - zerr("missing end of name", - NULL, 0); - data = 0; - } else { + } + break; + case 'g': + /* Given gid or group id... works like `u' */ + func = qualgid; + /* either the actual gid... */ + if (idigit(*s)) + data = qgetnum(&s); + else { + /* ...or a delimited group name. */ + char sav, *tt; + + tt = get_strarg(s); + if (!*tt) { + zerr("missing end of name", + NULL, 0); + data = 0; + } else { #ifdef HAVE_GETGRNAM - struct group *gr; - sav = *tt; - *tt = '\0'; - - if ((gr = getgrnam(s + 1))) - data = gr->gr_gid; - else { - zerr("unknown group", NULL, 0); - data = 0; - } - *tt = sav; -#else /* !HAVE_GETGRNAM */ - sav = *tt; + struct group *gr; + sav = *tt; + *tt = '\0'; + + if ((gr = getgrnam(s + 1))) + data = gr->gr_gid; + else { zerr("unknown group", NULL, 0); data = 0; -#endif /* !HAVE_GETGRNAM */ - if (sav) - s = tt + 1; - else - s = tt; } + *tt = sav; +#else /* !HAVE_GETGRNAM */ + sav = *tt; + zerr("unknown group", NULL, 0); + data = 0; +#endif /* !HAVE_GETGRNAM */ + if (sav) + s = tt + 1; + else + s = tt; } - break; - case 'f': - /* Match modes with chmod-spec. */ - func = qualmodeflags; - data = qgetmodespec(&s); - break; - case 'M': - /* Mark directories with a / */ - if ((gf_markdirs = !(sense & 1))) - gf_follow = sense & 2; - break; - case 'T': - /* Mark types in a `ls -F' type fashion */ - if ((gf_listtypes = !(sense & 1))) - gf_follow = sense & 2; - break; - case 'N': - /* Nullglob: remove unmatched patterns. */ - gf_nullglob = !(sense & 1); - break; - case 'D': - /* Glob dots: match leading dots implicitly */ - gf_noglobdots = sense & 1; - break; - case 'n': - /* Numeric glob sort */ - gf_numsort = !(sense & 1); - break; - case 'a': - /* Access time in given range */ - g_amc = 0; - func = qualtime; - goto getrange; - case 'm': - /* Modification time in given range */ - g_amc = 1; - func = qualtime; - goto getrange; - case 'c': - /* Inode creation time in given range */ - g_amc = 2; - func = qualtime; - goto getrange; - case 'L': - /* File size (Length) in given range */ - func = qualsize; - g_amc = -1; - /* Get size multiplier */ - g_units = TT_BYTES; - if (*s == 'p' || *s == 'P') - g_units = TT_POSIX_BLOCKS, ++s; - else if (*s == 'k' || *s == 'K') - g_units = TT_KILOBYTES, ++s; - else if (*s == 'm' || *s == 'M') - g_units = TT_MEGABYTES, ++s; - getrange: - /* Get time multiplier */ - if (g_amc >= 0) { - g_units = TT_DAYS; - if (*s == 'h') - g_units = TT_HOURS, ++s; - else if (*s == 'm') - g_units = TT_MINS, ++s; - else if (*s == 'w') - g_units = TT_WEEKS, ++s; - else if (*s == 'M') - g_units = TT_MONTHS, ++s; - else if (*s == 's') - g_units = TT_SECONDS, ++s; - } - /* See if it's greater than, equal to, or less than */ - if ((g_range = *s == '+' ? 1 : *s == '-' ? -1 : 0)) - ++s; - data = qgetnum(&s); - break; + } + break; + case 'f': + /* Match modes with chmod-spec. */ + func = qualmodeflags; + data = qgetmodespec(&s); + break; + case 'F': + func = qualnonemptydir; + break; + case 'M': + /* Mark directories with a / */ + if ((gf_markdirs = !(sense & 1))) + gf_follow = sense & 2; + break; + case 'T': + /* Mark types in a `ls -F' type fashion */ + if ((gf_listtypes = !(sense & 1))) + gf_follow = sense & 2; + break; + case 'N': + /* Nullglob: remove unmatched patterns. */ + gf_nullglob = !(sense & 1); + break; + case 'D': + /* Glob dots: match leading dots implicitly */ + gf_noglobdots = sense & 1; + break; + case 'n': + /* Numeric glob sort */ + gf_numsort = !(sense & 1); + break; + case 'a': + /* Access time in given range */ + g_amc = 0; + func = qualtime; + goto getrange; + case 'm': + /* Modification time in given range */ + g_amc = 1; + func = qualtime; + goto getrange; + case 'c': + /* Inode creation time in given range */ + g_amc = 2; + func = qualtime; + goto getrange; + case 'L': + /* File size (Length) in given range */ + func = qualsize; + g_amc = -1; + /* Get size multiplier */ + g_units = TT_BYTES; + if (*s == 'p' || *s == 'P') + g_units = TT_POSIX_BLOCKS, ++s; + else if (*s == 'k' || *s == 'K') + g_units = TT_KILOBYTES, ++s; + else if (*s == 'm' || *s == 'M') + g_units = TT_MEGABYTES, ++s; + getrange: + /* Get time multiplier */ + if (g_amc >= 0) { + g_units = TT_DAYS; + if (*s == 'h') + g_units = TT_HOURS, ++s; + else if (*s == 'm') + g_units = TT_MINS, ++s; + else if (*s == 'w') + g_units = TT_WEEKS, ++s; + else if (*s == 'M') + g_units = TT_MONTHS, ++s; + else if (*s == 's') + g_units = TT_SECONDS, ++s; + } + /* See if it's greater than, equal to, or less than */ + if ((g_range = *s == '+' ? 1 : *s == '-' ? -1 : 0)) + ++s; + data = qgetnum(&s); + break; - case 'o': - case 'O': + case 'o': + case 'O': + { + int t; + + switch (*s) { + case 'n': t = GS_NAME; break; + case 'L': t = GS_SIZE; break; + case 'l': t = GS_LINKS; break; + case 'a': t = GS_ATIME; break; + case 'm': t = GS_MTIME; break; + case 'c': t = GS_CTIME; break; + case 'd': t = GS_DEPTH; break; + default: + zerr("unknown sort specifier", NULL, 0); + restore_globstate(saved); + return; + } + if ((sense & 2) && !(t & (GS_NAME|GS_DEPTH))) + t <<= GS_SHIFT; + if (gf_sorts & t) { + zerr("doubled sort specifier", NULL, 0); + restore_globstate(saved); + return; + } + gf_sorts |= t; + gf_sortlist[gf_nsorts++] = t | + (((sense & 1) ^ (s[-1] == 'O')) ? GS_DESC : 0); + s++; + break; + } + case '+': + case 'e': + { + char sav, *tt; + int plus; + + if (s[-1] == '+') { + plus = 0; + tt = s; + while (iident(*tt)) + tt++; + if (tt == s) { - int t; - - switch (*s) { - case 'n': t = GS_NAME; break; - case 'L': t = GS_SIZE; break; - case 'l': t = GS_LINKS; break; - case 'a': t = GS_ATIME; break; - case 'm': t = GS_MTIME; break; - case 'c': t = GS_CTIME; break; - default: - zerr("unknown sort specifier", NULL, 0); - restore_globstate(saved); - return; - } - if ((sense & 2) && t != GS_NAME) - t <<= GS_SHIFT; - if (gf_sorts & t) { - zerr("doubled sort specifier", NULL, 0); - restore_globstate(saved); - return; - } - gf_sorts |= t; - gf_sortlist[gf_nsorts++] = t | - (((sense & 1) ^ (s[-1] == 'O')) ? GS_DESC : 0); - s++; - break; + zerr("missing identifier after `+'", NULL, 0); + tt = NULL; } - case 'e': + } else { + plus = 1; + tt = get_strarg(s); + if (!*tt) { - char sav, *tt = get_strarg(s); - - if (!*tt) { - zerr("missing end of string", NULL, 0); - data = 0; - } else { - sav = *tt; - *tt = '\0'; - func = qualsheval; - sdata = dupstring(s + 1); - untokenize(sdata); - *tt = sav; - if (sav) - s = tt + 1; - else - s = tt; - } - break; + zerr("missing end of string", NULL, 0); + tt = NULL; } - case '[': - case Inbrack: - { - char *os = --s; - struct value v; - - v.isarr = SCANPM_WANTVALS; - v.pm = NULL; - v.b = -1; - v.inv = 0; - if (getindex(&s, &v) || s == os) { - zerr("invalid subscript", NULL, 0); - restore_globstate(saved); - return; - } - first = v.a; - last = v.b; - break; - } - default: - zerr("unknown file attribute", NULL, 0); + } + + if (tt == NULL) { + data = 0; + } else { + sav = *tt; + *tt = '\0'; + func = qualsheval; + sdata = dupstring(s + plus); + untokenize(sdata); + *tt = sav; + if (sav) + s = tt + plus; + else + s = tt; + } + break; + } + case '[': + case Inbrack: + { + char *os = --s; + struct value v; + + v.isarr = SCANPM_WANTVALS; + v.pm = NULL; + v.end = -1; + v.inv = 0; + if (getindex(&s, &v, 0) || s == os) { + zerr("invalid subscript", NULL, 0); restore_globstate(saved); return; } - if (func) { - /* Requested test is performed by function func */ - if (!qn) - qn = (struct qual *)hcalloc(sizeof *qn); - if (ql) - ql->next = qn; - ql = qn; - if (!quals) - quals = qo = qn; - qn->func = func; - qn->sense = sense; - qn->data = data; - qn->sdata = sdata; - qn->range = g_range; - qn->units = g_units; - qn->amc = g_amc; - qn = NULL; - qualct++; + first = v.start; + end = v.end; + break; } - if (errflag) { + default: + zerr("unknown file attribute", NULL, 0); restore_globstate(saved); return; } } + if (func) { + /* Requested test is performed by function func */ + if (!qn) + qn = (struct qual *)hcalloc(sizeof *qn); + if (ql) + ql->next = qn; + ql = qn; + if (!newquals) + newquals = qo = qn; + qn->func = func; + qn->sense = sense; + qn->data = data; + qn->sdata = sdata; + qn->range = g_range; + qn->units = g_units; + qn->amc = g_amc; + + qn = NULL; + qualct++; + } + if (errflag) { + restore_globstate(saved); + return; + } } + + if (quals && newquals) { + /* Merge previous group of qualifiers with new set. */ + if (quals->or || newquals->or) { + /* The hard case. */ + struct qual *qorhead = NULL, *qortail = NULL; + /* + * Distribute in the most trivial way, by creating + * all possible combinations of the two sets and chaining + * these into one long set of alternatives given + * by qorhead and qortail. + */ + for (qn = newquals; qn; qn = qn->or) { + for (qo = quals; qo; qo = qo->or) { + struct qual *qfirst, *qlast; + int islast = !qn->or && !qo->or; + /* Generate first set of qualifiers... */ + if (islast) { + /* Last time round: don't bother copying. */ + qfirst = qn; + for (qlast = qfirst; qlast->next; + qlast = qlast->next) + ; + } else + qfirst = dup_qual_list(qn, &qlast); + /* ... link into new `or' chain ... */ + if (!qorhead) + qorhead = qfirst; + if (qortail) + qortail->or = qfirst; + qortail = qfirst; + /* ... and concatenate second set. */ + qlast->next = islast ? qo : dup_qual_list(qo, NULL); + } + } + quals = qorhead; + } else { + /* + * Easy: we can just chain the qualifiers together. + * This is an optimisation; the code above will work, too. + * We retain the original left to right ordering --- remember + * we are searching for sets of qualifiers from the right. + */ + qn = newquals; + for ( ; newquals->next; newquals = newquals->next) + ; + newquals->next = quals; + quals = qn; + } + } else if (newquals) + quals = newquals; } q = parsepat(str); if (!q || errflag) { /* if parsing failed */ @@ -1387,6 +1612,7 @@ glob(LinkList list, LinkNode np, int nountok) } else if (isset(NOMATCH)) { zerr("no matches found: %s", ostr, 0); free(matchbuf); + restore_globstate(saved); return; } else { /* treat as an ordinary string */ @@ -1400,18 +1626,18 @@ glob(LinkList list, LinkNode np, int nountok) qsort((void *) & matchbuf[0], matchct, sizeof(struct gmatch), (int (*) _((const void *, const void *)))gmatchcmp); - if (first < 0) + if (first < 0) { first += matchct; - if (last < 0) - last += matchct; - if (first < 0) - first = 0; - if (last >= matchct) - last = matchct - 1; - if (first <= last) { - matchptr = matchbuf + matchct - 1 - last; - last -= first; - while (last-- >= 0) { /* insert matches in the arg list */ + if (first < 0) + first = 0; + } + if (end < 0) + end += matchct + 1; + else if (end > matchct) + end = matchct; + if ((end -= first) > 0) { + matchptr = matchbuf + matchct - first - end; + while (end-- > 0) { /* insert matches in the arg list */ insertlinknode(list, node, matchptr->name); matchptr++; } @@ -1421,46 +1647,6 @@ glob(LinkList list, LinkNode np, int nountok) restore_globstate(saved); } -/* Return the order of two strings, taking into account * - * possible numeric order if NUMERICGLOBSORT is set. * - * The comparison here is reversed. */ - -/**/ -static int -notstrcmp(char **a, char **b) -{ - char *c = *b, *d = *a; - int cmp; - -#ifdef HAVE_STRCOLL - cmp = strcoll(c, d); -#endif - for (; *c == *d && *c; c++, d++); -#ifndef HAVE_STRCOLL - cmp = (int)STOUC(*c) - (int)STOUC(*d); -#endif - if (gf_numsort && (idigit(*c) || idigit(*d))) { - for (; c > *b && idigit(c[-1]); c--, d--); - if (idigit(*c) && idigit(*d)) { - while (*c == '0') - c++; - while (*d == '0') - d++; - for (; idigit(*c) && *c == *d; c++, d++); - if (idigit(*c) || idigit(*d)) { - cmp = (int)STOUC(*c) - (int)STOUC(*d); - while (idigit(*c) && idigit(*d)) - c++, d++; - if (idigit(*c) && !idigit(*d)) - return 1; - if (idigit(*d) && !idigit(*c)) - return -1; - } - } - } - return cmp; -} - /* Return the trailing character for marking file types */ /**/ @@ -1488,7 +1674,7 @@ file_type(mode_t filemode) /* check to see if str is eligible for brace expansion */ /**/ -int +mod_export int hasbraces(char *str) { char *lbr, *mbr, *comma; @@ -1601,27 +1787,27 @@ xpandredir(struct redir *fn, LinkList tab) char *s = peekfirst(&fake); fn->name = s; untokenize(s); - if (fn->type == MERGEIN || fn->type == MERGEOUT) { + if (fn->type == REDIR_MERGEIN || fn->type == REDIR_MERGEOUT) { if (s[0] == '-' && !s[1]) - fn->type = CLOSE; - else if (s[0] == 'p' && !s[1]) + fn->type = REDIR_CLOSE; + else if (s[0] == 'p' && !s[1]) fn->fd2 = -2; else { while (idigit(*s)) s++; if (!*s && s > fn->name) fn->fd2 = zstrtol(fn->name, NULL, 10); - else if (fn->type == MERGEIN) + else if (fn->type == REDIR_MERGEIN) zerr("file number expected", NULL, 0); else - fn->type = ERRWRITE; + fn->type = REDIR_ERRWRITE; } } - } else if (fn->type == MERGEIN) + } else if (fn->type == REDIR_MERGEIN) zerr("file number expected", NULL, 0); else { - if (fn->type == MERGEOUT) - fn->type = ERRWRITE; + if (fn->type == REDIR_MERGEOUT) + fn->type = REDIR_ERRWRITE; while ((nam = (char *)ugetnode(&fake))) { /* Loop over matches, duplicating the * * redirection for each file found. */ @@ -1635,42 +1821,10 @@ xpandredir(struct redir *fn, LinkList tab) return ret; } -/* concatenate s1 and s2 in dynamically allocated buffer */ - -/**/ -mod_export char * -dyncat(char *s1, char *s2) -{ - /* This version always uses space from the current heap. */ - char *ptr; - int l1 = strlen(s1); - - ptr = (char *)zhalloc(l1 + strlen(s2) + 1); - strcpy(ptr, s1); - strcpy(ptr + l1, s2); - return ptr; -} - -/* concatenate s1, s2, and s3 in dynamically allocated buffer */ - -/**/ -mod_export char * -tricat(char const *s1, char const *s2, char const *s3) -{ - /* This version always uses permanently-allocated space. */ - char *ptr; - - ptr = (char *)zalloc(strlen(s1) + strlen(s2) + strlen(s3) + 1); - strcpy(ptr, s1); - strcat(ptr, s2); - strcat(ptr, s3); - return ptr; -} - /* brace expansion */ /**/ -void +mod_export void xpandbraces(LinkList list, LinkNode *np) { LinkNode node = (*np), last = prevnode(node); @@ -1701,7 +1855,7 @@ xpandbraces(LinkList list, LinkNode *np) int rstart = zstrtol(str+1,&dots,10), rend = 0, err = 0, rev = 0; int wid1 = (dots - str) - 1, wid2 = (str2 - dots) - 2; int strp = str - str3; - + if (dots == str + 1 || *dots != '.' || dots[1] != '.') err++; else { @@ -1742,12 +1896,13 @@ xpandbraces(LinkList list, LinkNode *np) * set of flags saying whether each character is present; * * the final list is in lexical order. */ char ccl[256], *p; - unsigned char c1, c2, lastch; + unsigned char c1, c2; unsigned int len, pl; + int lastch = -1; uremnode(list, node); memset(ccl, 0, sizeof(ccl) / sizeof(ccl[0])); - for (p = str + 1, lastch = 0; p < str2;) { + for (p = str + 1; p < str2;) { if (itok(c1 = *p++)) c1 = ztokens[c1 - STOUC(Pound)]; if ((char) c1 == Meta) @@ -1756,10 +1911,10 @@ xpandbraces(LinkList list, LinkNode *np) c2 = ztokens[c2 - STOUC(Pound)]; if ((char) c2 == Meta) c2 = 32 ^ p[1]; - if (c1 == '-' && lastch && p < str2 && (int)lastch <= (int)c2) { - while ((int)lastch < (int)c2) + if (c1 == '-' && lastch >= 0 && p < str2 && lastch <= (int)c2) { + while (lastch < (int)c2) ccl[lastch++] = 1; - lastch = 0; + lastch = -1; } else ccl[lastch = c1] = 1; } @@ -1844,7 +1999,7 @@ struct repldata { }; typedef struct repldata *Repldata; -/* +/* * List of bits of matches to concatenate with replacement string. * The data is a struct repldata. It is not used in cases like * ${...//#foo/bar} even though SUB_GLOBAL is set, since the match @@ -2029,21 +2184,74 @@ getmatcharr(char ***ap, char *pat, int fl, int n, char *replstr) pp++; } +/**/ +static void +set_pat_start(Patprog p, int offs) +{ + /* + * If we are messing around with the test string by advancing up + * it from the start, we need to tell the pattern matcher that + * a start-of-string assertion, i.e. (#s), should fail. Hence + * we test whether the offset of the real start of string from + * the actual start, passed as offs, is zero. + */ + if (offs) + p->flags |= PAT_NOTSTART; + else + p->flags &= ~PAT_NOTSTART; +} + +/**/ +static void +set_pat_end(Patprog p, char null_me) +{ + /* + * If we are messing around with the string by shortening it at the + * tail, we need to tell the pattern matcher that an end-of-string + * assertion, i.e. (#e), should fail. Hence we test whether + * the character null_me about to be zapped is or is not already a null. + */ + if (null_me) + p->flags |= PAT_NOTEND; + else + p->flags &= ~PAT_NOTEND; +} + /**/ static int igetmatch(char **sp, Patprog p, int fl, int n, char *replstr) { - char *s = *sp, *t, sav; - int i, l = strlen(*sp), ml = ztrlen(*sp), matched = 1; + char *s = *sp, *t; + /* + * Note that ioff and ml count characters in the character + * set (Meta's are not included), while l counts characters in the + * string. + */ + int ioff, l = strlen(*sp), ml = ztrlen(*sp), matched = 1; repllist = NULL; /* perform must-match test for complex closures */ - if (p->mustoff && !strstr((char *)s, (char *)p + p->mustoff)) - matched = 0; + if (p->mustoff) + { + /* + * Yuk. Probably we should rewrite this whole function to + * use an unmetafied test string. + * + * Use META_HEAPDUP because we need a terminating NULL. + */ + char *muststr = metafy((char *)p + p->mustoff, + p->patmlen, META_HEAPDUP); + + if (!strstr(s, muststr)) + matched = 0; + } + + /* in case we used the prog before... */ + p->flags &= ~(PAT_NOTSTART|PAT_NOTEND); if (fl & SUB_ALL) { - i = matched && pattry(p, s); + int i = matched && pattry(p, s); *sp = get_match_ret(*sp, 0, i ? l : 0, fl, i ? replstr : 0); if (! **sp && (((fl & SUB_MATCH) && !i) || ((fl & SUB_REST) && i))) return 0; @@ -2058,24 +2266,22 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr) * First get the longest match... */ if (pattry(p, s)) { - char *mpos = patinput; + /* patmatchlen returns metafied length, as we need */ + int mlen = patmatchlen(); if (!(fl & SUB_LONG) && !(p->flags & PAT_PURES)) { /* * ... now we know whether it's worth looking for the * shortest, which we do by brute force. */ - for (t = s; t < mpos; METAINC(t)) { - sav = *t; - *t = '\0'; - if (pattry(p, s)) { - mpos = patinput; - *t = sav; + for (t = s; t < s + mlen; METAINC(t)) { + set_pat_end(p, *t); + if (pattrylen(p, s, t - s, 0)) { + mlen = patmatchlen(); break; } - *t = sav; } } - *sp = get_match_ret(*sp, 0, mpos-s, fl, replstr); + *sp = get_match_ret(*sp, 0, mlen, fl, replstr); return 1; } break; @@ -2084,37 +2290,35 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr) /* Smallest possible match at tail of string: * * move back down string until we get a match. * * There's no optimization here. */ - patoffset = ml; - for (t = s + l; t >= s; t--, patoffset--) { - if (pattry(p, t)) { + for (ioff = ml, t = s + l; t >= s; t--, ioff--) { + set_pat_start(p, t-s); + if (pattrylen(p, t, -1, ioff)) { *sp = get_match_ret(*sp, t - s, l, fl, replstr); - patoffset = 0; return 1; } if (t > s+1 && t[-2] == Meta) t--; } - patoffset = 0; break; case (SUB_END|SUB_LONG): /* Largest possible match at tail of string: * * move forward along string until we get a match. * * Again there's no optimisation. */ - for (i = 0, t = s; i < l; i++, t++, patoffset++) { - if (pattry(p, t)) { - *sp = get_match_ret(*sp, i, l, fl, replstr); - patoffset = 0; + for (ioff = 0, t = s; t < s + l; ioff++, t++) { + set_pat_start(p, t-s); + if (pattrylen(p, t, -1, ioff)) { + *sp = get_match_ret(*sp, t-s, l, fl, replstr); return 1; } if (*t == Meta) - i++, t++; + t++; } - patoffset = 0; break; case SUB_SUBSTR: /* Smallest at start, but matching substrings. */ + set_pat_start(p, l); if (!(fl & SUB_GLOBAL) && pattry(p, s + l) && !--n) { *sp = get_match_ret(*sp, 0, 0, fl, replstr); return 1; @@ -2124,24 +2328,23 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr) t = s; if (fl & SUB_GLOBAL) repllist = newlinklist(); + ioff = 0; /* offset into string */ do { /* loop over all matches for global substitution */ matched = 0; - for (; t < s + l; t++, patoffset++) { + for (; t < s + l; t++, ioff++) { /* Find the longest match from this position. */ - if (pattry(p, t) && patinput > t) { - char *mpos = patinput; + set_pat_start(p, t-s); + if (pattrylen(p, t, -1, ioff)) { + char *mpos = t + patmatchlen(); if (!(fl & SUB_LONG) && !(p->flags & PAT_PURES)) { char *ptr; for (ptr = t; ptr < mpos; METAINC(ptr)) { - sav = *ptr; - *ptr = '\0'; - if (pattry(p, t)) { - mpos = patinput; - *ptr = sav; + set_pat_end(p, *ptr); + if (pattrylen(p, t, ptr - t, ioff)) { + mpos = t + patmatchlen(); break; } - *ptr = sav; } } if (!--n || (n <= 0 && (fl & SUB_GLOBAL))) { @@ -2159,7 +2362,6 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr) */ continue; } else { - patoffset = 0; return 1; } } @@ -2168,7 +2370,7 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr) * which is already marked for replacement. */ matched = 1; - for ( ; t < mpos; t++, patoffset++) + for ( ; t < mpos; t++, ioff++) if (*t == Meta) t++; break; @@ -2177,12 +2379,12 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr) t++; } } while (matched); - patoffset = 0; /* * check if we can match a blank string, if so do it * at the start. Goodness knows if this is a good idea * with global substitution, so it doesn't happen. */ + set_pat_start(p, l); if ((fl & (SUB_LONG|SUB_GLOBAL)) == SUB_LONG && pattry(p, s + l) && !--n) { *sp = get_match_ret(*sp, 0, 0, fl, replstr); @@ -2191,47 +2393,41 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr) break; case (SUB_END|SUB_SUBSTR): - /* Shortest at end with substrings */ - patoffset = ml; - if (pattry(p, s + l) && !--n) { - *sp = get_match_ret(*sp, l, l, fl, replstr); - patoffset = 0; - return 1; - } /* fall through */ case (SUB_END|SUB_LONG|SUB_SUBSTR): /* Longest/shortest at end, matching substrings. */ - patoffset--; - for (t = s + l - 1; t >= s; t--, patoffset--) { + if (!(fl & SUB_LONG)) { + set_pat_start(p, l); + if (pattrylen(p, s + l, -1, ml) && !--n) { + *sp = get_match_ret(*sp, l, l, fl, replstr); + return 1; + } + } + for (ioff = ml - 1, t = s + l - 1; t >= s; t--, ioff--) { if (t > s && t[-1] == Meta) t--; - if (pattry(p, t) && patinput > t && !--n) { + set_pat_start(p, t-s); + if (pattrylen(p, t, -1, ioff) && !--n) { /* Found the longest match */ - char *mpos = patinput; + char *mpos = t + patmatchlen(); if (!(fl & SUB_LONG) && !(p->flags & PAT_PURES)) { char *ptr; for (ptr = t; ptr < mpos; METAINC(ptr)) { - sav = *ptr; - *ptr = '\0'; - if (pattry(p, t)) { - mpos = patinput; - *ptr = sav; + set_pat_end(p, *ptr); + if (pattrylen(p, t, ptr - t, ioff)) { + mpos = t + patmatchlen(); break; } - *ptr = sav; } } *sp = get_match_ret(*sp, t-s, mpos-s, fl, replstr); - patoffset = 0; return 1; } } - patoffset = ml; - if ((fl & SUB_LONG) && pattry(p, s + l) && !--n) { + set_pat_start(p, l); + if ((fl & SUB_LONG) && pattrylen(p, s + l, -1, ml) && !--n) { *sp = get_match_ret(*sp, l, l, fl, replstr); - patoffset = 0; return 1; } - patoffset = 0; break; } } @@ -2242,6 +2438,7 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr) Repldata rd; int lleft = 0; /* size of returned string */ char *ptr, *start; + int i; i = 0; /* start of last chunk we got from *sp */ for (nd = firstnode(repllist); nd; incnode(nd)) { @@ -2278,6 +2475,20 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr) /**/ mod_export void tokenize(char *s) +{ + zshtokenize(s, 0); +} + +/**/ +mod_export void +shtokenize(char *s) +{ + zshtokenize(s, isset(SHGLOB)); +} + +/**/ +static void +zshtokenize(char *s, int shglob) { char *t; int bslash = 0; @@ -2294,7 +2505,7 @@ tokenize(char *s) bslash = 1; continue; case '<': - if (isset(SHGLOB)) + if (shglob) break; if (bslash) { s[-1] = Bnull; @@ -2313,8 +2524,9 @@ tokenize(char *s) case '(': case '|': case ')': - if (isset(SHGLOB)) + if (shglob) break; + case '>': case '^': case '#': case '~': @@ -2322,6 +2534,7 @@ tokenize(char *s) case ']': case '*': case '?': + case '=': for (t = ztokens; *t; t++) if (*t == *s) { if (bslash) @@ -2367,16 +2580,16 @@ remnulargs(char *s) /**/ static int -qualdev(char *name, struct stat *buf, off_t dv, char *dummy) +qualdev(UNUSED(char *name), struct stat *buf, off_t dv, UNUSED(char *dummy)) { - return buf->st_dev == dv; + return (off_t)buf->st_dev == dv; } /* number of hard links to file */ /**/ static int -qualnlink(char *name, struct stat *buf, off_t ct, char *dummy) +qualnlink(UNUSED(char *name), struct stat *buf, off_t ct, UNUSED(char *dummy)) { return (g_range < 0 ? buf->st_nlink < ct : g_range > 0 ? buf->st_nlink > ct : @@ -2387,7 +2600,7 @@ qualnlink(char *name, struct stat *buf, off_t ct, char *dummy) /**/ static int -qualuid(char *name, struct stat *buf, off_t uid, char *dummy) +qualuid(UNUSED(char *name), struct stat *buf, off_t uid, UNUSED(char *dummy)) { return buf->st_uid == uid; } @@ -2396,7 +2609,7 @@ qualuid(char *name, struct stat *buf, off_t uid, char *dummy) /**/ static int -qualgid(char *name, struct stat *buf, off_t gid, char *dummy) +qualgid(UNUSED(char *name), struct stat *buf, off_t gid, UNUSED(char *dummy)) { return buf->st_gid == gid; } @@ -2405,7 +2618,7 @@ qualgid(char *name, struct stat *buf, off_t gid, char *dummy) /**/ static int -qualisdev(char *name, struct stat *buf, off_t junk, char *dummy) +qualisdev(UNUSED(char *name), struct stat *buf, UNUSED(off_t junk), UNUSED(char *dummy)) { return S_ISBLK(buf->st_mode) || S_ISCHR(buf->st_mode); } @@ -2414,7 +2627,7 @@ qualisdev(char *name, struct stat *buf, off_t junk, char *dummy) /**/ static int -qualisblk(char *name, struct stat *buf, off_t junk, char *dummy) +qualisblk(UNUSED(char *name), struct stat *buf, UNUSED(off_t junk), UNUSED(char *dummy)) { return S_ISBLK(buf->st_mode); } @@ -2423,7 +2636,7 @@ qualisblk(char *name, struct stat *buf, off_t junk, char *dummy) /**/ static int -qualischr(char *name, struct stat *buf, off_t junk, char *dummy) +qualischr(UNUSED(char *name), struct stat *buf, UNUSED(off_t junk), UNUSED(char *dummy)) { return S_ISCHR(buf->st_mode); } @@ -2432,7 +2645,7 @@ qualischr(char *name, struct stat *buf, off_t junk, char *dummy) /**/ static int -qualisdir(char *name, struct stat *buf, off_t junk, char *dummy) +qualisdir(UNUSED(char *name), struct stat *buf, UNUSED(off_t junk), UNUSED(char *dummy)) { return S_ISDIR(buf->st_mode); } @@ -2441,7 +2654,7 @@ qualisdir(char *name, struct stat *buf, off_t junk, char *dummy) /**/ static int -qualisfifo(char *name, struct stat *buf, off_t junk, char *dummy) +qualisfifo(UNUSED(char *name), struct stat *buf, UNUSED(off_t junk), UNUSED(char *dummy)) { return S_ISFIFO(buf->st_mode); } @@ -2450,7 +2663,7 @@ qualisfifo(char *name, struct stat *buf, off_t junk, char *dummy) /**/ static int -qualislnk(char *name, struct stat *buf, off_t junk, char *dummy) +qualislnk(UNUSED(char *name), struct stat *buf, UNUSED(off_t junk), UNUSED(char *dummy)) { return S_ISLNK(buf->st_mode); } @@ -2459,7 +2672,7 @@ qualislnk(char *name, struct stat *buf, off_t junk, char *dummy) /**/ static int -qualisreg(char *name, struct stat *buf, off_t junk, char *dummy) +qualisreg(UNUSED(char *name), struct stat *buf, UNUSED(off_t junk), UNUSED(char *dummy)) { return S_ISREG(buf->st_mode); } @@ -2468,7 +2681,7 @@ qualisreg(char *name, struct stat *buf, off_t junk, char *dummy) /**/ static int -qualissock(char *name, struct stat *buf, off_t junk, char *dummy) +qualissock(UNUSED(char *name), struct stat *buf, UNUSED(off_t junk), UNUSED(char *dummy)) { return S_ISSOCK(buf->st_mode); } @@ -2477,7 +2690,7 @@ qualissock(char *name, struct stat *buf, off_t junk, char *dummy) /**/ static int -qualflags(char *name, struct stat *buf, off_t mod, char *dummy) +qualflags(UNUSED(char *name), struct stat *buf, off_t mod, UNUSED(char *dummy)) { return mode_to_octal(buf->st_mode) & mod; } @@ -2486,7 +2699,7 @@ qualflags(char *name, struct stat *buf, off_t mod, char *dummy) /**/ static int -qualmodeflags(char *name, struct stat *buf, off_t mod, char *dummy) +qualmodeflags(UNUSED(char *name), struct stat *buf, off_t mod, UNUSED(char *dummy)) { long v = mode_to_octal(buf->st_mode), y = mod & 07777, n = mod >> 12; @@ -2497,7 +2710,7 @@ qualmodeflags(char *name, struct stat *buf, off_t mod, char *dummy) /**/ static int -qualiscom(char *name, struct stat *buf, off_t mod, char *dummy) +qualiscom(UNUSED(char *name), struct stat *buf, UNUSED(off_t mod), UNUSED(char *dummy)) { return S_ISREG(buf->st_mode) && (buf->st_mode & S_IXUGO); } @@ -2506,7 +2719,7 @@ qualiscom(char *name, struct stat *buf, off_t mod, char *dummy) /**/ static int -qualsize(char *name, struct stat *buf, off_t size, char *dummy) +qualsize(UNUSED(char *name), struct stat *buf, off_t size, UNUSED(char *dummy)) { #if defined(LONG_IS_64_BIT) || defined(OFF_T_IS_64_BIT) # define QS_CAST_SIZE() @@ -2541,7 +2754,7 @@ qualsize(char *name, struct stat *buf, off_t size, char *dummy) /**/ static int -qualtime(char *name, struct stat *buf, off_t days, char *dummy) +qualtime(UNUSED(char *name), struct stat *buf, off_t days, UNUSED(char *dummy)) { time_t now, diff; @@ -2576,11 +2789,11 @@ qualtime(char *name, struct stat *buf, off_t days, char *dummy) /**/ static int -qualsheval(char *name, struct stat *buf, off_t days, char *str) +qualsheval(char *name, UNUSED(struct stat *buf), UNUSED(off_t days), char *str) { Eprog prog; - if ((prog = parse_string(str, 0))) { + if ((prog = parse_string(str))) { int ef = errflag, lv = lastval, ret; unsetparam("reply"); @@ -2609,3 +2822,30 @@ qualsheval(char *name, struct stat *buf, off_t days, char *str) } return 0; } + +/**/ +static int +qualnonemptydir(char *name, struct stat *buf, UNUSED(off_t days), UNUSED(char *str)) +{ + DIR *dirh; + struct dirent *de; + + if (!S_ISDIR(buf->st_mode)) + return 0; + + if (buf->st_nlink > 2) + return 1; + + if (!(dirh = opendir(name))) + return 0; + + while ((de = readdir(dirh))) { + if (strcmp(de->d_name, ".") && strcmp(de->d_name, "..")) { + closedir(dirh); + return 1; + } + } + + closedir(dirh); + return 0; +} diff --git a/Src/params.c b/Src/params.c index cdaecb199..7f91bd048 100644 --- a/Src/params.c +++ b/Src/params.c @@ -56,7 +56,6 @@ char **path, /* $path */ /**/ char *argzero, /* $0 */ *home, /* $HOME */ - *hostnam, /* $HOST */ *nullcmd, /* $NULLCMD */ *oldpwd, /* $OLDPWD */ *zoptarg, /* $OPTARG */ @@ -66,14 +65,15 @@ char *argzero, /* $0 */ *prompt4, /* $PROMPT4 */ *readnullcmd, /* $READNULLCMD */ *rprompt, /* $RPROMPT */ + *rprompt2, /* $RPROMPT2 */ *sprompt, /* $SPROMPT */ - *term, /* $TERM */ *wordchars, /* $WORDCHARS */ *zsh_name; /* $ZSH_NAME */ /**/ mod_export char *ifs, /* $IFS */ *postedit, /* $POSTEDIT */ + *term, /* $TERM */ *ttystrname, /* $TTY */ *pwd; /* $PWD */ @@ -97,7 +97,9 @@ mod_export unsigned char bangchar; /**/ unsigned char hatchar, hashchar; -/* $SECONDS = time(NULL) - shtimer.tv_sec */ +/* $SECONDS = now.tv_sec - shtimer.tv_sec + * + (now.tv_usec - shtimer.tv_usec) / 1000000.0 + * (rounded to an integer if the parameter is not set to float) */ /**/ struct timeval shtimer; @@ -106,59 +108,164 @@ struct timeval shtimer; /**/ mod_export int termflags; - + +/* Standard methods for get/set/unset pointers in parameters */ + +/**/ +mod_export const struct gsu_scalar stdscalar_gsu = +{ strgetfn, strsetfn, stdunsetfn }; +/**/ +mod_export const struct gsu_scalar varscalar_gsu = +{ strvargetfn, strvarsetfn, stdunsetfn }; +/**/ +mod_export const struct gsu_scalar nullsetscalar_gsu = +{ strgetfn, nullstrsetfn, NULL }; + +/**/ +mod_export const struct gsu_integer stdinteger_gsu = +{ intgetfn, intsetfn, stdunsetfn }; +/**/ +mod_export const struct gsu_integer varinteger_gsu = +{ intvargetfn, intvarsetfn, stdunsetfn }; +/**/ +mod_export const struct gsu_integer nullsetinteger_gsu = +{ intgetfn, NULL, NULL }; + +/**/ +mod_export const struct gsu_float stdfloat_gsu = +{ floatgetfn, floatsetfn, stdunsetfn }; + +/**/ +mod_export const struct gsu_array stdarray_gsu = +{ arrgetfn, arrsetfn, stdunsetfn }; +/**/ +mod_export const struct gsu_array vararray_gsu = +{ arrvargetfn, arrvarsetfn, stdunsetfn }; + +/**/ +mod_export const struct gsu_hash stdhash_gsu = +{ hashgetfn, hashsetfn, stdunsetfn }; +/**/ +mod_export const struct gsu_hash nullsethash_gsu = +{ hashgetfn, nullsethashfn, nullunsetfn }; + + +/* Non standard methods (not exported) */ +static const struct gsu_integer pound_gsu = +{ poundgetfn, nullintsetfn, stdunsetfn }; +static const struct gsu_integer errno_gsu = +{ errnogetfn, errnosetfn, stdunsetfn }; +static const struct gsu_integer gid_gsu = +{ gidgetfn, gidsetfn, stdunsetfn }; +static const struct gsu_integer egid_gsu = +{ egidgetfn, egidsetfn, stdunsetfn }; +static const struct gsu_integer histsize_gsu = +{ histsizegetfn, histsizesetfn, stdunsetfn }; +static const struct gsu_integer random_gsu = +{ randomgetfn, randomsetfn, stdunsetfn }; +static const struct gsu_integer savehist_gsu = +{ savehistsizegetfn, savehistsizesetfn, stdunsetfn }; +static const struct gsu_integer intseconds_gsu = +{ intsecondsgetfn, intsecondssetfn, stdunsetfn }; +static const struct gsu_float floatseconds_gsu = +{ floatsecondsgetfn, floatsecondssetfn, stdunsetfn }; +static const struct gsu_integer uid_gsu = +{ uidgetfn, uidsetfn, stdunsetfn }; +static const struct gsu_integer euid_gsu = +{ euidgetfn, euidsetfn, stdunsetfn }; +static const struct gsu_integer ttyidle_gsu = +{ ttyidlegetfn, nullintsetfn, stdunsetfn }; + +static const struct gsu_scalar username_gsu = +{ usernamegetfn, usernamesetfn, stdunsetfn }; +static const struct gsu_scalar dash_gsu = +{ dashgetfn, nullstrsetfn, stdunsetfn }; +static const struct gsu_scalar histchars_gsu = +{ histcharsgetfn, histcharssetfn, stdunsetfn }; +static const struct gsu_scalar home_gsu = +{ homegetfn, homesetfn, stdunsetfn }; +static const struct gsu_scalar term_gsu = +{ termgetfn, termsetfn, stdunsetfn }; +static const struct gsu_scalar wordchars_gsu = +{ wordcharsgetfn, wordcharssetfn, stdunsetfn }; +static const struct gsu_scalar ifs_gsu = +{ ifsgetfn, ifssetfn, stdunsetfn }; +static const struct gsu_scalar underscore_gsu = +{ underscoregetfn, nullstrsetfn, stdunsetfn }; +#ifdef USE_LOCALE +static const struct gsu_scalar lc_blah_gsu = +{ strgetfn, lcsetfn, stdunsetfn }; +static const struct gsu_scalar lang_gsu = +{ strgetfn, langsetfn, stdunsetfn }; +static const struct gsu_scalar lc_all_gsu = +{ strgetfn, lc_allsetfn, stdunsetfn }; +#endif + +static const struct gsu_integer varint_readonly_gsu = +{ intvargetfn, nullintsetfn, stdunsetfn }; +static const struct gsu_integer zlevar_gsu = +{ intvargetfn, zlevarsetfn, stdunsetfn }; + +static const struct gsu_scalar colonarr_gsu = +{ colonarrgetfn, colonarrsetfn, stdunsetfn }; + +static const struct gsu_integer argc_gsu = +{ poundgetfn, nullintsetfn, stdunsetfn }; +static const struct gsu_array pipestatus_gsu = +{ pipestatgetfn, pipestatsetfn, stdunsetfn }; + /* Nodes for special parameters for parameter hash table */ -static #ifdef HAVE_UNION_INIT # define BR(X) {X} -struct param +typedef struct param initparam; #else # define BR(X) X -struct iparam { +typedef struct iparam { struct hashnode *next; char *nam; /* hash data */ int flags; /* PM_* flags (defined in zsh.h) */ void *value; - void (*func1) _((void)); /* set func */ - char *(*func2) _((void)); /* get func */ - void (*unsetfn) _((Param, int)); /* unset func */ - int ct; /* output base or field width */ + void *gsu; /* get/set/unset methods */ + int base; /* output base */ + int width; /* output field width */ char *env; /* location in environment, if exported */ char *ename; /* name of corresponding environment var */ Param old; /* old struct for use with local */ int level; /* if (old != NULL), level of localness */ -} +} initparam; #endif -special_params[] ={ -#define SFN(X) BR(((void (*)_((Param, char *)))(X))) -#define GFN(X) BR(((char *(*)_((Param)))(X))) -#define IPDEF1(A,B,C,D) {NULL,A,PM_INTEGER|PM_SPECIAL|D,BR(NULL),SFN(C),GFN(B),stdunsetfn,10,NULL,NULL,NULL,0} -IPDEF1("#", poundgetfn, nullsetfn, PM_READONLY), -IPDEF1("ERRNO", errnogetfn, nullsetfn, PM_READONLY), -IPDEF1("GID", gidgetfn, gidsetfn, PM_DONTIMPORT | PM_RESTRICTED), -IPDEF1("EGID", egidgetfn, egidsetfn, PM_DONTIMPORT | PM_RESTRICTED), -IPDEF1("HISTSIZE", histsizegetfn, histsizesetfn, PM_RESTRICTED), -IPDEF1("RANDOM", randomgetfn, randomsetfn, 0), -IPDEF1("SECONDS", secondsgetfn, secondssetfn, 0), -IPDEF1("UID", uidgetfn, uidsetfn, PM_DONTIMPORT | PM_RESTRICTED), -IPDEF1("EUID", euidgetfn, euidsetfn, PM_DONTIMPORT | PM_RESTRICTED), -IPDEF1("TTYIDLE", ttyidlegetfn, nullsetfn, PM_READONLY), - -#define IPDEF2(A,B,C,D) {NULL,A,PM_SCALAR|PM_SPECIAL|D,BR(NULL),SFN(C),GFN(B),stdunsetfn,0,NULL,NULL,NULL,0} -IPDEF2("USERNAME", usernamegetfn, usernamesetfn, PM_DONTIMPORT|PM_RESTRICTED), -IPDEF2("-", dashgetfn, nullsetfn, PM_READONLY), -IPDEF2("histchars", histcharsgetfn, histcharssetfn, PM_DONTIMPORT), -IPDEF2("HOME", homegetfn, homesetfn, 0), -IPDEF2("TERM", termgetfn, termsetfn, 0), -IPDEF2("WORDCHARS", wordcharsgetfn, wordcharssetfn, 0), -IPDEF2("IFS", ifsgetfn, ifssetfn, PM_DONTIMPORT), -IPDEF2("_", underscoregetfn, nullsetfn, PM_READONLY), + +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("ERRNO", errno_gsu, 0), +IPDEF1("GID", gid_gsu, PM_DONTIMPORT | PM_RESTRICTED), +IPDEF1("EGID", egid_gsu, PM_DONTIMPORT | PM_RESTRICTED), +IPDEF1("HISTSIZE", histsize_gsu, PM_RESTRICTED), +IPDEF1("RANDOM", random_gsu, 0), +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), + +#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("histchars", histchars_gsu, PM_DONTIMPORT), +IPDEF2("HOME", home_gsu, 0), +IPDEF2("TERM", term_gsu, 0), +IPDEF2("WORDCHARS", wordchars_gsu, 0), +IPDEF2("IFS", ifs_gsu, PM_DONTIMPORT), +IPDEF2("_", underscore_gsu, PM_READONLY), #ifdef USE_LOCALE -# define LCIPDEF(name) IPDEF2(name, strgetfn, lcsetfn, PM_UNSET) -IPDEF2("LANG", strgetfn, langsetfn, PM_UNSET), -IPDEF2("LC_ALL", strgetfn, lc_allsetfn, PM_UNSET), +# define LCIPDEF(name) IPDEF2(name, lc_blah_gsu, PM_UNSET) +IPDEF2("LANG", lang_gsu, PM_UNSET), +IPDEF2("LC_ALL", lc_all_gsu, PM_UNSET), # ifdef LC_COLLATE LCIPDEF("LC_COLLATE"), # endif @@ -176,34 +283,38 @@ LCIPDEF("LC_TIME"), # endif #endif /* USE_LOCALE */ -#define IPDEF4(A,B) {NULL,A,PM_INTEGER|PM_READONLY|PM_SPECIAL,BR((void *)B),SFN(nullsetfn),GFN(intvargetfn),stdunsetfn,10,NULL,NULL,NULL,0} +#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} IPDEF4("!", &lastpid), IPDEF4("$", &mypid), IPDEF4("?", &lastval), +IPDEF4("HISTCMD", &curhist), IPDEF4("LINENO", &lineno), IPDEF4("PPID", &ppid), -#define IPDEF5(A,B,F) {NULL,A,PM_INTEGER|PM_SPECIAL,BR((void *)B),SFN(F),GFN(intvargetfn),stdunsetfn,10,NULL,NULL,NULL,0} -IPDEF5("COLUMNS", &columns, zlevarsetfn), -IPDEF5("LINES", &lines, zlevarsetfn), -IPDEF5("OPTIND", &zoptind, intvarsetfn), -IPDEF5("SHLVL", &shlvl, intvarsetfn), +#define IPDEF5(A,B,F) {NULL,A,PM_INTEGER|PM_SPECIAL,BR((void *)B),GSU(varinteger_gsu),10,0,NULL,NULL,NULL,0} +IPDEF5("COLUMNS", &columns, zlevar_gsu), +IPDEF5("LINES", &lines, zlevar_gsu), +IPDEF5("OPTIND", &zoptind, varinteger_gsu), +IPDEF5("SHLVL", &shlvl, varinteger_gsu), +IPDEF5("TRY_BLOCK_ERROR", &try_errflag, varinteger_gsu), -#define IPDEF7(A,B) {NULL,A,PM_SCALAR|PM_SPECIAL,BR((void *)B),SFN(strvarsetfn),GFN(strvargetfn),stdunsetfn,0,NULL,NULL,NULL,0} +#define IPDEF7(A,B) {NULL,A,PM_SCALAR|PM_SPECIAL,BR((void *)B),GSU(varscalar_gsu),0,0,NULL,NULL,NULL,0} IPDEF7("OPTARG", &zoptarg), IPDEF7("NULLCMD", &nullcmd), IPDEF7("POSTEDIT", &postedit), IPDEF7("READNULLCMD", &readnullcmd), -IPDEF7("RPROMPT", &rprompt), IPDEF7("PS1", &prompt), +IPDEF7("RPS1", &rprompt), +IPDEF7("RPROMPT", &rprompt), IPDEF7("PS2", &prompt2), +IPDEF7("RPS2", &rprompt2), +IPDEF7("RPROMPT2", &rprompt2), IPDEF7("PS3", &prompt3), IPDEF7("PS4", &prompt4), -IPDEF7("RPS1", &rprompt), IPDEF7("SPROMPT", &sprompt), IPDEF7("0", &argzero), -#define IPDEF8(A,B,C,D) {NULL,A,D|PM_SCALAR|PM_SPECIAL,BR((void *)B),SFN(colonarrsetfn),GFN(colonarrgetfn),stdunsetfn,0,NULL,C,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), @@ -215,16 +326,18 @@ IPDEF8("PSVAR", &psvar, "psvar", 0), /* MODULE_PATH is not imported for security reasons */ IPDEF8("MODULE_PATH", &module_path, "module_path", PM_DONTIMPORT|PM_RESTRICTED), -#define IPDEF9F(A,B,C,D) {NULL,A,D|PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT,BR((void *)B),SFN(arrvarsetfn),GFN(arrvargetfn),stdunsetfn,0,NULL,C,NULL,0} +#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) -IPDEF9("*", &pparams, NULL), -IPDEF9("@", &pparams, NULL), -{NULL, NULL}, - -/* The following parameters are not avaible in sh/ksh compatibility * - * mode. All of these has sh compatible equivalents. */ -IPDEF1("ARGC", poundgetfn, nullsetfn, PM_READONLY), -IPDEF2("HISTCHARS", histcharsgetfn, histcharssetfn, PM_DONTIMPORT), +IPDEF9F("*", &pparams, NULL, PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT|PM_READONLY), +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 IPDEF10(A,B) {NULL,A,PM_ARRAY|PM_SPECIAL,BR(NULL),GSU(B),10,0,NULL,NULL,NULL,0} + +/* The following parameters are not available in sh/ksh compatibility * + * mode. All of these have sh compatible equivalents. */ +IPDEF1("ARGC", argc_gsu, PM_READONLY), +IPDEF2("HISTCHARS", histchars_gsu, PM_DONTIMPORT), IPDEF4("status", &lastval), IPDEF7("prompt", &prompt), IPDEF7("PROMPT", &prompt), @@ -244,8 +357,19 @@ IPDEF9("watch", &watch, "WATCH"), IPDEF9F("module_path", &module_path, "MODULE_PATH", PM_RESTRICTED), IPDEF9F("path", &path, "PATH", PM_RESTRICTED), -{NULL, NULL} +IPDEF10("pipestatus", pipestatus_gsu), + +{NULL,NULL,0,BR(NULL),NULL_GSU,0,0,NULL,NULL,NULL,0}, }; + +/* + * Special way of referring to the positional parameters. Unlike $* + * and $@, this is not readonly. This parameter is not directly + * visible in user space. + */ +initparam argvparam_pm = IPDEF9F("", &pparams, NULL, \ + PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT); + #undef BR #define IS_UNSET_VALUE(V) \ @@ -263,7 +387,10 @@ mod_export HashTable paramtab, realparamtab; mod_export HashTable newparamtable(int size, char const *name) { - HashTable ht = newhashtable(size, name, NULL); + HashTable ht; + if (!size) + size = 17; + ht = newhashtable(size, name, NULL); ht->hash = hasher; ht->emptytable = emptyhashtable; @@ -308,11 +435,11 @@ static HashTable outtable; /**/ static void -scancopyparams(HashNode hn, int flags) +scancopyparams(HashNode hn, UNUSED(int flags)) { /* Going into a real parameter, so always use permanent storage */ Param pm = (Param)hn; - Param tpm = (Param) zcalloc(sizeof *tpm); + Param tpm = (Param) zshcalloc(sizeof *tpm); tpm->nam = ztrdup(pm->nam); copyparam(tpm, pm, 0); addhashnode(outtable, tpm->nam, tpm); @@ -351,7 +478,7 @@ static unsigned numparamvals; /**/ mod_export void -scancountparams(HashNode hn, int flags) +scancountparams(UNUSED(HashNode hn), int flags) { ++numparamvals; if ((flags & SCANPM_WANTKEYS) && (flags & SCANPM_WANTVALS)) @@ -361,6 +488,7 @@ scancountparams(HashNode hn, int flags) static Patprog scanprog; static char *scanstr; static char **paramvals; +static Param foundparam; /**/ void @@ -384,6 +512,7 @@ scanparamvals(HashNode hn, int flags) } else if ((flags & SCANPM_MATCHKEY) && !pattry(scanprog, v.pm->nam)) { return; } + foundparam = v.pm; if (flags & SCANPM_WANTKEYS) { paramvals[numparamvals++] = v.pm->nam; if (!(flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL))) @@ -391,8 +520,8 @@ scanparamvals(HashNode hn, int flags) } v.isarr = (PM_TYPE(v.pm->flags) & (PM_ARRAY|PM_HASHED)); v.inv = 0; - v.a = 0; - v.b = -1; + v.start = 0; + v.end = -1; paramvals[numparamvals] = getstrvalue(&v); if (flags & SCANPM_MATCHVAL) { if (pattry(scanprog, paramvals[numparamvals])) { @@ -430,17 +559,43 @@ getvaluearr(Value v) if (v->arr) return v->arr; else if (PM_TYPE(v->pm->flags) == PM_ARRAY) - return v->arr = v->pm->gets.afn(v->pm); + return v->arr = v->pm->gsu.a->getfn(v->pm); else if (PM_TYPE(v->pm->flags) == PM_HASHED) { - v->arr = paramvalarr(v->pm->gets.hfn(v->pm), v->isarr); + v->arr = paramvalarr(v->pm->gsu.h->getfn(v->pm), v->isarr); /* Can't take numeric slices of associative arrays */ - v->a = 0; - v->b = numparamvals; + v->start = 0; + v->end = numparamvals + 1; return v->arr; } else return NULL; } +/* + * Split environment string into (name, value) pair. + * this is used to avoid in-place editing of environment table + * that results in core dump on some systems + */ + +static int +split_env_string(char *env, char **name, char **value) +{ + char *str, *tenv; + + if (!env || !name || !value) + return 0; + + tenv = strcpy(zhalloc(strlen(env) + 1), env); + for (str = tenv; *str && *str != '='; str++) + ; + if (str != tenv && *str == '=') { + *str = '\0'; + *name = tenv; + *value = str + 1; + return 1; + } else + return 0; +} + /* Set up parameter hash table. This will add predefined * * parameter entries as well as setting up parameter table * * entries for environment variables we inherit. */ @@ -450,9 +605,13 @@ void createparamtable(void) { Param ip, pm; - char **new_environ, **envp, **envp2, **sigptr, **t; - char buf[50], *str, *iname; - int num_env, oae = opts[ALLEXPORT]; +#ifndef HAVE_PUTENV + char **new_environ; + int envsize; +#endif + char **envp, **envp2, **sigptr, **t; + char buf[50], *str, *iname, *ivalue, *hostnam; + int oae = opts[ALLEXPORT]; #ifdef HAVE_UNAME struct utsname unamebuf; char *machinebuf; @@ -467,13 +626,13 @@ createparamtable(void) while ((++ip)->nam) paramtab->addnode(paramtab, ztrdup(ip->nam), ip); - argvparam = (Param) paramtab->getnode(paramtab, "*"); + argvparam = (Param) &argvparam_pm; noerrs = 2; /* Add the standard non-special parameters which have to * * be initialized before we copy the environment variables. * - * We don't want to override whatever values the users has * + * We don't want to override whatever values the user has * * given them in the environment. */ opts[ALLEXPORT] = 0; setiparam("MAILCHECK", 60); @@ -487,72 +646,75 @@ createparamtable(void) setsparam("TMPPREFIX", ztrdup(DEFAULT_TMPPREFIX)); setsparam("TIMEFMT", ztrdup(DEFAULT_TIMEFMT)); setsparam("WATCHFMT", ztrdup(default_watchfmt)); + + hostnam = (char *)zalloc(256); + gethostname(hostnam, 256); setsparam("HOST", ztrdup(hostnam)); + zfree(hostnam, 256); + setsparam("LOGNAME", ztrdup((str = getlogin()) && *str ? str : cached_username)); +#ifndef HAVE_PUTENV /* Copy the environment variables we are inheriting to dynamic * * memory, so we can do mallocs and frees on it. */ - num_env = arrlen(environ); - new_environ = (char **) zalloc(sizeof(char *) * (num_env + 1)); - *new_environ = NULL; + envsize = sizeof(char *)*(1 + arrlen(environ)); + new_environ = (char **) zalloc(envsize); + memcpy(new_environ, environ, envsize); + environ = new_environ; +#endif + + /* Use heap allocation to avoid many small alloc/free calls */ + pushheap(); /* Now incorporate environment variables we are inheriting * - * into the parameter hash table. */ - for (envp = new_environ, envp2 = environ; *envp2; envp2++) { - for (str = *envp2; *str && *str != '='; str++); - if (*str == '=') { - iname = NULL; - *str = '\0'; - if (!idigit(**envp2) && isident(*envp2) && !strchr(*envp2, '[')) { - iname = *envp2; + * into the parameter hash table. Copy them into dynamic * + * memory so that we can free them if needed */ + for (envp = envp2 = environ; *envp2; envp2++) { + if (split_env_string(*envp2, &iname, &ivalue)) { + if (!idigit(*iname) && isident(iname) && !strchr(iname, '[')) { if ((!(pm = (Param) paramtab->getnode(paramtab, iname)) || - !(pm->flags & PM_DONTIMPORT)) && - (pm = setsparam(iname, metafy(str + 1, -1, META_DUP))) && - !(pm->flags & PM_EXPORTED)) { - *str = '='; + !(pm->flags & PM_DONTIMPORT || pm->flags & PM_EXPORTED)) && + (pm = setsparam(iname, metafy(ivalue, -1, META_DUP)))) { pm->flags |= PM_EXPORTED; - pm->env = *envp++ = ztrdup(*envp2); - *envp = NULL; if (pm->flags & PM_SPECIAL) - pm->env = replenv(pm->env, getsparam(pm->nam), - pm->flags); + pm->env = mkenvstr (pm->nam, + getsparam(pm->nam), pm->flags); + else + pm->env = ztrdup(*envp2); + *envp++ = pm->env; } } - *str = '='; } } - environ = new_environ; + popheap(); + *envp = '\0'; opts[ALLEXPORT] = oae; pm = (Param) paramtab->getnode(paramtab, "HOME"); - if (!(pm->flags & PM_EXPORTED)) { - pm->flags |= PM_EXPORTED; - pm->env = addenv("HOME", home, pm->flags); - } + if (!(pm->flags & PM_EXPORTED)) + addenv(pm, home); pm = (Param) paramtab->getnode(paramtab, "LOGNAME"); - if (!(pm->flags & PM_EXPORTED)) { - pm->flags |= PM_EXPORTED; - pm->env = addenv("LOGNAME", pm->u.str, pm->flags); - } - pm = (Param) paramtab->getnode(paramtab, "SHLVL"); if (!(pm->flags & PM_EXPORTED)) - pm->flags |= PM_EXPORTED; + addenv(pm, pm->u.str); + pm = (Param) paramtab->getnode(paramtab, "SHLVL"); sprintf(buf, "%d", (int)++shlvl); - pm->env = addenv("SHLVL", buf, pm->flags); + /* shlvl value in environment needs updating unconditionally */ + addenv(pm, buf); /* Add the standard non-special parameters */ set_pwd_env(); #ifdef HAVE_UNAME - if(uname(&unamebuf)) setsparam("MACHTYPE", ztrdup(MACHTYPE)); + if(uname(&unamebuf)) setsparam("CPUTYPE", ztrdup("unknown")); else { machinebuf = ztrdup(unamebuf.machine); - setsparam("MACHTYPE", machinebuf); + setsparam("CPUTYPE", machinebuf); } #else - setsparam("MACHTYPE", ztrdup(MACHTYPE)); + setsparam("CPUTYPE", ztrdup("unknown")); #endif + setsparam("MACHTYPE", ztrdup(MACHTYPE)); setsparam("OSTYPE", ztrdup(OSTYPE)); setsparam("TTY", ztrdup(ttystrname)); setsparam("VENDOR", ztrdup(VENDOR)); @@ -572,31 +734,25 @@ assigngetset(Param pm) { switch (PM_TYPE(pm->flags)) { case PM_SCALAR: - pm->sets.cfn = strsetfn; - pm->gets.cfn = strgetfn; + pm->gsu.s = &stdscalar_gsu; break; case PM_INTEGER: - pm->sets.ifn = intsetfn; - pm->gets.ifn = intgetfn; + pm->gsu.i = &stdinteger_gsu; break; case PM_EFLOAT: case PM_FFLOAT: - pm->sets.ffn = floatsetfn; - pm->gets.ffn = floatgetfn; + pm->gsu.f = &stdfloat_gsu; break; case PM_ARRAY: - pm->sets.afn = arrsetfn; - pm->gets.afn = arrgetfn; + pm->gsu.a = &stdarray_gsu; break; case PM_HASHED: - pm->sets.hfn = hashsetfn; - pm->gets.hfn = hashgetfn; + pm->gsu.h = &stdhash_gsu; break; default: DPUTS(1, "BUG: tried to create param node without valid flag"); break; } - pm->unsetfn = stdunsetfn; } /* Create a parameter, so that it can be assigned to. Returns NULL if the * @@ -613,16 +769,25 @@ createparam(char *name, int flags) { Param pm, oldpm; + if (paramtab != realparamtab) + flags = (flags & ~PM_EXPORTED) | PM_HASHELEM; + if (name != nulstring) { oldpm = (Param) (paramtab == realparamtab ? gethashnode2(paramtab, name) : paramtab->getnode(paramtab, name)); DPUTS(oldpm && oldpm->level > locallevel, - "BUG: old local parameter not deleteed"); + "BUG: old local parameter not deleted"); if (oldpm && (oldpm->level == locallevel || !(flags & PM_LOCAL))) { if (!(oldpm->flags & PM_UNSET) || (oldpm->flags & PM_SPECIAL)) { oldpm->flags &= ~PM_UNSET; + if ((oldpm->flags & PM_SPECIAL) && oldpm->ename) { + Param altpm = + (Param) paramtab->getnode(paramtab, oldpm->ename); + if (altpm) + altpm->flags &= ~PM_UNSET; + } return NULL; } if ((oldpm->flags & PM_RESTRICTED) && isset(RESTRICTED)) { @@ -631,21 +796,26 @@ createparam(char *name, int flags) } pm = oldpm; - pm->ct = 0; + pm->base = pm->width = 0; oldpm = pm->old; } else { - pm = (Param) zcalloc(sizeof *pm); + pm = (Param) zshcalloc(sizeof *pm); if ((pm->old = oldpm)) { - /* needed to avoid freeing oldpm */ + /* + * needed to avoid freeing oldpm, but we do take it + * out of the environment when it's hidden. + */ + if (oldpm->env) + delenv(oldpm); paramtab->removenode(paramtab, name); } paramtab->addnode(paramtab, ztrdup(name), pm); } - if (isset(ALLEXPORT) && !oldpm) + if (isset(ALLEXPORT) && !(flags & PM_HASHELEM)) flags |= PM_EXPORTED; } else { - pm = (Param) zhalloc(sizeof *pm); + pm = (Param) hcalloc(sizeof *pm); pm->nam = nulstring; } pm->flags = flags & ~PM_LOCAL; @@ -668,24 +838,26 @@ copyparam(Param tpm, Param pm, int toplevel) * with sets.?fn() usage). */ tpm->flags = pm->flags; + tpm->base = pm->base; + tpm->width = pm->width; if (!toplevel) tpm->flags &= ~PM_SPECIAL; switch (PM_TYPE(pm->flags)) { case PM_SCALAR: - tpm->u.str = ztrdup(pm->gets.cfn(pm)); + tpm->u.str = ztrdup(pm->gsu.s->getfn(pm)); break; case PM_INTEGER: - tpm->u.val = pm->gets.ifn(pm); + tpm->u.val = pm->gsu.i->getfn(pm); break; case PM_EFLOAT: case PM_FFLOAT: - tpm->u.dval = pm->gets.ffn(pm); + tpm->u.dval = pm->gsu.f->getfn(pm); break; case PM_ARRAY: - tpm->u.arr = zarrdup(pm->gets.afn(pm)); + tpm->u.arr = zarrdup(pm->gsu.a->getfn(pm)); break; case PM_HASHED: - tpm->u.hash = copyparamtable(pm->gets.hfn(pm), pm->nam); + tpm->u.hash = copyparamtable(pm->gsu.h->getfn(pm), pm->nam); break; } /* @@ -702,7 +874,7 @@ copyparam(Param tpm, Param pm, int toplevel) /* Return 1 if the string s is a valid identifier, else return 0. */ /**/ -int +mod_export int isident(char *s) { char *ss; @@ -712,43 +884,30 @@ isident(char *s) if (!*s) /* empty string is definitely not valid */ return 0; - /* find the first character in `s' not in the iident type table */ - for (ss = s; *ss; ss++) - if (!iident(*ss)) - break; - -#if 0 - /* If this exhaust `s' or the next two characters * - * are [(, then it is a valid identifier. */ - if (!*ss || (*ss == '[' && ss[1] == '(')) - return 1; - - /* Else if the next character is not [, then it is * - * definitely not a valid identifier. */ - if (*ss != '[') - return 0; + if (idigit(*s)) { + /* If the first character is `s' is a digit, then all must be */ + for (ss = ++s; *ss; ss++) + if (!idigit(*ss)) + break; + } else { + /* Find the first character in `s' not in the iident type table */ + for (ss = s; *ss; ss++) + if (!iident(*ss)) + break; + } - noeval = 1; - (void)mathevalarg(++ss, &ss); - if (*ss == ',') - (void)mathevalarg(++ss, &ss); - noeval = ne; /* restore the value of noeval */ - if (*ss != ']' || ss[1]) - return 0; - return 1; -#else /* If the next character is not [, then it is * - * definitely not a valid identifier. */ + * definitely not a valid identifier. */ if (!*ss) return 1; if (*ss != '[') return 0; - /* Require balanced [ ] pairs */ - if (skipparens('[', ']', &ss)) + /* Require balanced [ ] pairs with something between */ + if (!(ss = parse_subscript(++ss, 1))) return 0; - return !*ss; -#endif + untokenize(s); + return !ss[1]; } /**/ @@ -805,7 +964,8 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w) sep = "\n"; break; case 'e': - /* obsolate compatibility flag without any real effect */ + /* Compatibility flag with no effect except to prevent * + * special interpretation by getindex() of `*' or `@'. */ break; case 'n': t = get_strarg(++s); @@ -836,7 +996,7 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w) break; case 's': /* This gives the string that separates words * - * (for use with the `w' flag. */ + * (for use with the `w' flag). */ t = get_strarg(++s); if (!*t) goto flagerr; @@ -880,8 +1040,24 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w) } for (t = s, i = 0; - (c = *t) && ((c != ']' && c != Outbrack && + (c = *t) && ((c != Outbrack && (ishash || c != ',')) || i); t++) { + /* Untokenize INULL() except before brackets and double-quotes */ + if (INULL(c)) { + c = t[1]; + if (c == '[' || c == ']' || + c == '(' || c == ')' || + c == '{' || c == '}') { + /* This test handles nested subscripts in hash keys */ + if (ishash && i) + *t = ztokens[*t - Pound]; + needtok = 1; + ++t; + } else if (c != '"') + *t = ztokens[*t - Pound]; + continue; + } + /* Inbrack and Outbrack are probably never found here ... */ if (c == '[' || c == Inbrack) i++; else if (c == ']' || c == Outbrack) @@ -893,17 +1069,24 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w) return 0; s = dupstrpfx(s, t - s); *str = tt = t; + /* If we're NOT reverse subscripting, strip the INULL()s so brackets * + * are not backslashed after parsestr(). Otherwise leave them alone * + * so that the brackets will be escaped when we patcompile() or when * + * subscript arithmetic is performed (for nested subscripts). */ + if (ishash && (keymatch || !rev)) + remnulargs(s); if (needtok) { if (parsestr(s)) return 0; singsub(&s); - } + } else if (rev) + remnulargs(s); /* This is probably always a no-op, but ... */ if (!rev) { if (ishash) { - HashTable ht = v->pm->gets.hfn(v->pm); + HashTable ht = v->pm->gsu.h->getfn(v->pm); if (!ht) { ht = newparamtable(17, v->pm->nam); - v->pm->sets.hfn(v->pm, ht); + v->pm->gsu.h->setfn(v->pm, ht); } untokenize(s); if (!(v->pm = (Param) ht->getnode(ht, s))) { @@ -913,13 +1096,15 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w) paramtab = tht; } v->isarr = (*inv ? SCANPM_WANTINDEX : 0); - v->a = 0; + v->start = 0; *inv = 0; /* We've already obtained the "index" (key) */ - *w = v->b = -1; + *w = v->end = -1; r = isset(KSHARRAYS) ? 1 : 0; - } else - if (!(r = mathevalarg(s, &s)) || (isset(KSHARRAYS) && r >= 0)) - r++; + } else { + r = mathevalarg(s, &s); + if (isset(KSHARRAYS) && r >= 0) + r++; + } if (word && !v->isarr) { s = t = getstrvalue(v); i = wordcount(s, sep, 0); @@ -936,7 +1121,7 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w) return 0; if (!a2 && *tt != ',') - *w = (zlong)(s - t) - 1; + *w = (zlong)(s - t); return (a2 ? s : d + 1) - t; } else if (!v->isarr && !word) { @@ -964,7 +1149,7 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w) s = d; } } else { - if (!l || s[l - 1] != '*') { + if (!l || s[l - 1] != '*' || (l > 1 && s[l - 2] == '\\')) { d = (char *) hcalloc(l + 2); strcpy(d, s); strcat(d, "*"); @@ -972,7 +1157,10 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w) } } } - tokenize(s); + if (!keymatch) { + tokenize(s); + remnulargs(s); + } if (keymatch || (pprog = patcompile(s, 0, NULL))) { int len; @@ -981,10 +1169,9 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w) if (ishash) { scanprog = pprog; scanstr = s; - if (keymatch) { - untokenize(s); + if (keymatch) v->isarr |= SCANPM_KEYMATCH; - } else if (ind) + else if (ind) v->isarr |= SCANPM_MATCHKEY; else v->isarr |= SCANPM_MATCHVAL; @@ -995,7 +1182,7 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w) (v->isarr & (SCANPM_MATCHKEY | SCANPM_MATCHVAL | SCANPM_KEYMATCH))))) { *inv = v->inv; - *w = v->b; + *w = v->end; return 1; } } else @@ -1046,7 +1233,7 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w) if (!--r) { r = (zlong)(t - s + (a2 ? -1 : 1)); if (!a2 && *tt != ',') - *w = r + strlen(ta[i]) - 2; + *w = r + strlen(ta[i]) - 1; return r; } return a2 ? -1 : 0; @@ -1058,10 +1245,12 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w) if (beg < 0) beg += len; if (beg >= 0 && beg < len) { + char *de = d + len; + if (a2) { if (down) { if (!hasbeg) - beg = len - 1; + beg = len; for (r = beg, t = d + beg; t >= d; r--, t--) { sav = *t; *t = '\0'; @@ -1073,7 +1262,7 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w) *t = sav; } } else - for (r = beg, t = d + beg; *t; r++, t++) { + for (r = beg, t = d + beg; t <= de; r++, t++) { sav = *t; *t = '\0'; if (pattry(pprog, d) && @@ -1086,20 +1275,20 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w) } else { if (down) { if (!hasbeg) - beg = len - 1; + beg = len; for (r = beg + 1, t = d + beg; t >= d; r--, t--) { if (pattry(pprog, t) && !--num) return r; } } else - for (r = beg + 1, t = d + beg; *t; r++, t++) + for (r = beg + 1, t = d + beg; t <= de; r++, t++) if (pattry(pprog, t) && !--num) return r; } } - return 0; + return down ? 0 : len + 1; } } } @@ -1108,81 +1297,97 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w) /**/ int -getindex(char **pptr, Value v) +getindex(char **pptr, Value v, int dq) { - int a, b, inv = 0; + int start, end, inv = 0; char *s = *pptr, *tbrack; *s++ = '['; - for (tbrack = s; *tbrack && *tbrack != ']' && *tbrack != Outbrack; tbrack++) - if (itok(*tbrack)) + s = parse_subscript(s, dq); /* Error handled after untokenizing */ + /* Now we untokenize everything except INULL() markers so we can check * + * for the '*' and '@' special subscripts. The INULL()s are removed * + * in getarg() after we know whether we're doing reverse indexing. */ + for (tbrack = *pptr + 1; *tbrack && tbrack != s; tbrack++) { + if (INULL(*tbrack) && !*++tbrack) + break; + if (itok(*tbrack)) /* Need to check for Nularg here? */ *tbrack = ztokens[*tbrack - Pound]; - if (*tbrack == Outbrack) - *tbrack = ']'; - if ((s[0] == '*' || s[0] == '@') && s[1] == ']') { + } + /* If we reached the end of the string (s == NULL) we have an error */ + if (*tbrack) + *tbrack = Outbrack; + else { + zerr("invalid subscript", NULL, 0); + *pptr = tbrack; + return 1; + } + s = *pptr + 1; + if ((s[0] == '*' || s[0] == '@') && s + 1 == tbrack) { if ((v->isarr || IS_UNSET_VALUE(v)) && s[0] == '@') v->isarr |= SCANPM_ISVAR_AT; - v->a = 0; - v->b = -1; + v->start = 0; + v->end = -1; s += 2; } else { zlong we = 0, dummy; - a = getarg(&s, &inv, v, 0, &we); + start = getarg(&s, &inv, v, 0, &we); if (inv) { - if (!v->isarr && a != 0) { + if (!v->isarr && start != 0) { char *t, *p; t = getstrvalue(v); - if (a > 0) { - for (p = t + a - 1; p-- > t; ) + if (start > 0) { + for (p = t + start - 1; p-- > t; ) if (*p == Meta) - a--; + start--; } else - a = -ztrlen(t + a + strlen(t)); + start = -ztrlen(t + start + strlen(t)); } - if (a > 0 && (isset(KSHARRAYS) || (v->pm->flags & PM_HASHED))) - a--; + if (start > 0 && (isset(KSHARRAYS) || (v->pm->flags & PM_HASHED))) + start--; if (v->isarr != SCANPM_WANTINDEX) { v->inv = 1; v->isarr = 0; - v->a = v->b = a; + v->start = start; + v->end = start + 1; } if (*s == ',') { zerr("invalid subscript", NULL, 0); - while (*s != ']' && *s != Outbrack) - s++; - *pptr = s; + *tbrack = ']'; + *pptr = tbrack+1; return 1; } - if (*s == ']' || *s == Outbrack) + if (s == tbrack) s++; } else { int com; - if (a > 0) - a--; if ((com = (*s == ','))) { s++; - b = getarg(&s, &inv, v, 1, &dummy); - if (b > 0) - b--; + end = getarg(&s, &inv, v, 1, &dummy); } else { - b = we ? we : a; + end = we ? we : start; } - if (*s == ']' || *s == Outbrack) { + if (start != end) com = 1; + if (start > 0) + start--; + else if (start == 0 && end == 0) + end++; + if (s == tbrack) { s++; - if (v->isarr && a == b && !com && + if (v->isarr && !com && (!(v->isarr & SCANPM_MATCHMANY) || !(v->isarr & (SCANPM_MATCHKEY | SCANPM_MATCHVAL | SCANPM_KEYMATCH)))) v->isarr = 0; - v->a = a; - v->b = b; + v->start = start; + v->end = end; } else s = *pptr; } } + *tbrack = ']'; *pptr = s; return 0; } @@ -1239,7 +1444,8 @@ fetchvalue(Value v, char **pptr, int bracks, int flags) v = (Value) hcalloc(sizeof *v); v->pm = argvparam; v->inv = 0; - v->a = v->b = ppar - 1; + v->start = ppar - 1; + v->end = ppar; if (sav) *s = sav; } else { @@ -1269,28 +1475,34 @@ fetchvalue(Value v, char **pptr, int bracks, int flags) } v->pm = pm; v->inv = 0; - v->a = 0; - v->b = -1; + v->start = 0; + v->end = -1; if (bracks > 0 && (*s == '[' || *s == Inbrack)) { - if (getindex(&s, v)) { + if (getindex(&s, v, (flags & SCANPM_DQUOTED))) { *pptr = s; return v; } } else if (!(flags & SCANPM_ASSIGNING) && v->isarr && iident(*t) && isset(KSHARRAYS)) - v->b = 0, v->isarr = 0; + v->end = 1, v->isarr = 0; } if (!bracks && *s) return NULL; *pptr = s; - if (v->a > MAX_ARRLEN || - v->a < -MAX_ARRLEN) { - zerr("subscript too %s: %d", (v->a < 0) ? "small" : "big", v->a); + if (v->start > MAX_ARRLEN) { + zerr("subscript too %s: %d", "big", v->start + !isset(KSHARRAYS)); + return NULL; + } + if (v->start < -MAX_ARRLEN) { + zerr("subscript too %s: %d", "small", v->start); + return NULL; + } + if (v->end > MAX_ARRLEN+1) { + zerr("subscript too %s: %d", "big", v->end - !!isset(KSHARRAYS)); return NULL; } - if (v->b > MAX_ARRLEN || - v->b < -MAX_ARRLEN) { - zerr("subscript too %s: %d", (v->b < 0) ? "small" : "big", v->b); + if (v->end < -MAX_ARRLEN) { + zerr("subscript too %s: %d", "small", v->end); return NULL; } return v; @@ -1301,13 +1513,13 @@ mod_export char * getstrvalue(Value v) { char *s, **ss; - char buf[(sizeof(zlong) * 8) + 4]; + char buf[BDIGBUFSIZE]; if (!v) return hcalloc(1); if (v->inv && !(v->pm->flags & PM_HASHED)) { - sprintf(buf, "%d", v->a); + sprintf(buf, "%d", v->start); s = dupstring(buf); return s; } @@ -1317,7 +1529,7 @@ getstrvalue(Value v) /* (!v->isarr) should be impossible unless emulating ksh */ if (!v->isarr && emulation == EMULATE_KSH) { s = dupstring("[0]"); - if (getindex(&s, v) == 0) + if (getindex(&s, v, 0) == 0) s = getstrvalue(v); return s; } /* else fall through */ @@ -1326,21 +1538,23 @@ getstrvalue(Value v) if (v->isarr) s = sepjoin(ss, NULL, 1); else { - if (v->a < 0) - v->a += arrlen(ss); - s = (v->a >= arrlen(ss) || v->a < 0) ? (char *) hcalloc(1) : ss[v->a]; + if (v->start < 0) + v->start += arrlen(ss); + s = (v->start >= arrlen(ss) || v->start < 0) ? + (char *) hcalloc(1) : ss[v->start]; } return s; case PM_INTEGER: - convbase(buf, v->pm->gets.ifn(v->pm), v->pm->ct); + convbase(buf, v->pm->gsu.i->getfn(v->pm), v->pm->base); s = dupstring(buf); break; case PM_EFLOAT: case PM_FFLOAT: - s = convfloat(v->pm->gets.ffn(v->pm), v->pm->ct, v->pm->flags, NULL); + s = convfloat(v->pm->gsu.f->getfn(v->pm), + v->pm->base, v->pm->flags, NULL); break; case PM_SCALAR: - s = v->pm->gets.cfn(v->pm); + s = v->pm->gsu.s->getfn(v->pm); break; default: s = NULL; @@ -1348,18 +1562,18 @@ getstrvalue(Value v) break; } - if (v->a == 0 && v->b == -1) + if (v->start == 0 && v->end == -1) return s; - if (v->a < 0) - v->a += strlen(s); - if (v->b < 0) - v->b += strlen(s); - s = (v->a > (int)strlen(s)) ? dupstring("") : dupstring(s + v->a); - if (v->b < v->a) + if (v->start < 0) + v->start += strlen(s); + if (v->end < 0) + v->end += strlen(s) + 1; + s = (v->start > (int)strlen(s)) ? dupstring("") : dupstring(s + v->start); + if (v->end <= v->start) s[0] = '\0'; - else if (v->b - v->a < (int)strlen(s)) - s[v->b - v->a + 1 + (s[v->b - v->a] == Meta)] = '\0'; + else if (v->end - v->start <= (int)strlen(s)) + s[v->end - v->start + (s[v->end - v->start - 1] == Meta)] = '\0'; return s; } @@ -1367,7 +1581,7 @@ getstrvalue(Value v) static char *nular[] = {"", NULL}; /**/ -char ** +mod_export char ** getarrvalue(Value v) { char **s; @@ -1380,25 +1594,25 @@ getarrvalue(Value v) char buf[DIGBUFSIZE]; s = arrdup(nular); - sprintf(buf, "%d", v->a); + sprintf(buf, "%d", v->start); s[0] = dupstring(buf); return s; } s = getvaluearr(v); - if (v->a == 0 && v->b == -1) + if (v->start == 0 && v->end == -1) return s; - if (v->a < 0) - v->a += arrlen(s); - if (v->b < 0) - v->b += arrlen(s); - if (v->a > arrlen(s) || v->a < 0) + if (v->start < 0) + v->start += arrlen(s); + if (v->end < 0) + v->end += arrlen(s) + 1; + if (v->start > arrlen(s) || v->start < 0) s = arrdup(nular); else - s = arrdup(s + v->a); - if (v->b < v->a) + s = arrdup(s + v->start); + if (v->end <= v->start) s[0] = NULL; - else if (v->b - v->a < arrlen(s)) - s[v->b - v->a + 1] = NULL; + else if (v->end - v->start <= arrlen(s)) + s[v->end - v->start] = NULL; return s; } @@ -1409,11 +1623,11 @@ getintvalue(Value v) if (!v || v->isarr) return 0; if (v->inv) - return v->a; + return v->start; if (PM_TYPE(v->pm->flags) == PM_INTEGER) - return v->pm->gets.ifn(v->pm); + return v->pm->gsu.i->getfn(v->pm); if (v->pm->flags & (PM_EFLOAT|PM_FFLOAT)) - return (zlong)v->pm->gets.ffn(v->pm); + return (zlong)v->pm->gsu.f->getfn(v->pm); return mathevali(getstrvalue(v)); } @@ -1427,23 +1641,50 @@ getnumvalue(Value v) if (!v || v->isarr) { mn.u.l = 0; } else if (v->inv) { - mn.u.l = v->a; + mn.u.l = v->start; } else if (PM_TYPE(v->pm->flags) == PM_INTEGER) { - mn.u.l = v->pm->gets.ifn(v->pm); + mn.u.l = v->pm->gsu.i->getfn(v->pm); } else if (v->pm->flags & (PM_EFLOAT|PM_FFLOAT)) { mn.type = MN_FLOAT; - mn.u.d = v->pm->gets.ffn(v->pm); + mn.u.d = v->pm->gsu.f->getfn(v->pm); } else return matheval(getstrvalue(v)); return mn; } +/**/ +void +export_param(Param pm) +{ + char buf[BDIGBUFSIZE], *val; + + if (PM_TYPE(pm->flags) & (PM_ARRAY|PM_HASHED)) { +#if 0 /* Requires changes elsewhere in params.c and builtin.c */ + if (emulation == EMULATE_KSH /* isset(KSHARRAYS) */) { + struct value v; + v.isarr = 1; + v.inv = 0; + v.start = 0; + v.end = -1; + val = getstrvalue(&v); + } else +#endif + return; + } else if (PM_TYPE(pm->flags) == PM_INTEGER) + convbase(val = buf, pm->gsu.i->getfn(pm), pm->base); + else if (pm->flags & (PM_EFLOAT|PM_FFLOAT)) + val = convfloat(pm->gsu.f->getfn(pm), pm->base, + pm->flags, NULL); + else + val = pm->gsu.s->getfn(pm); + + addenv(pm, val); +} + /**/ mod_export void setstrvalue(Value v, char *val) { - char buf[(sizeof(zlong) * 8) + 4]; - if (v->pm->flags & PM_READONLY) { zerr("read-only variable: %s", v->pm->nam, 0); zsfree(val); @@ -1454,55 +1695,67 @@ setstrvalue(Value v, char *val) zsfree(val); return; } + if ((v->pm->flags & PM_HASHED) && (v->isarr & SCANPM_MATCHMANY)) { + zerr("%s: attempt to set slice of associative array", v->pm->nam, 0); + zsfree(val); + return; + } v->pm->flags &= ~PM_UNSET; switch (PM_TYPE(v->pm->flags)) { case PM_SCALAR: - if (v->a == 0 && v->b == -1) { - (v->pm->sets.cfn) (v->pm, val); - if (v->pm->flags & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z) && !v->pm->ct) - v->pm->ct = strlen(val); + if (v->start == 0 && v->end == -1) { + v->pm->gsu.s->setfn(v->pm, val); + if ((v->pm->flags & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z)) && + !v->pm->width) + v->pm->width = strlen(val); } else { char *z, *x; int zlen; - z = dupstring((v->pm->gets.cfn) (v->pm)); + z = dupstring(v->pm->gsu.s->getfn(v->pm)); zlen = strlen(z); if (v->inv && unset(KSHARRAYS)) - v->a--, v->b--; - if (v->a < 0) { - v->a += zlen; - if (v->a < 0) - v->a = 0; + v->start--, v->end--; + if (v->start < 0) { + v->start += zlen; + if (v->start < 0) + v->start = 0; } - if (v->a > zlen) - v->a = zlen; - if (v->b < 0) - v->b += zlen; - if (v->b > zlen - 1) - v->b = zlen - 1; - x = (char *) zalloc(v->a + strlen(val) + zlen - v->b); - strncpy(x, z, v->a); - strcpy(x + v->a, val); - strcat(x + v->a, z + v->b + 1); - (v->pm->sets.cfn) (v->pm, x); + if (v->start > zlen) + v->start = zlen; + if (v->end < 0) + v->end += zlen + 1; + else if (v->end > zlen) + v->end = zlen; + x = (char *) zalloc(v->start + strlen(val) + zlen - v->end + 1); + strncpy(x, z, v->start); + strcpy(x + v->start, val); + strcat(x + v->start, z + v->end); + v->pm->gsu.s->setfn(v->pm, x); zsfree(val); } break; case PM_INTEGER: if (val) { - (v->pm->sets.ifn) (v->pm, mathevali(val)); + v->pm->gsu.i->setfn(v->pm, mathevali(val)); zsfree(val); + if ((v->pm->flags & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z)) && + !v->pm->width) + v->pm->width = strlen(val); } - if (!v->pm->ct && lastbase != -1) - v->pm->ct = lastbase; + if (!v->pm->base && lastbase != -1) + v->pm->base = lastbase; break; case PM_EFLOAT: case PM_FFLOAT: if (val) { mnumber mn = matheval(val); - (v->pm->sets.ffn) (v->pm, (mn.type & MN_FLOAT) ? mn.u.d : + v->pm->gsu.f->setfn(v->pm, (mn.type & MN_FLOAT) ? mn.u.d : (double)mn.u.l); zsfree(val); + if ((v->pm->flags & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z)) && + !v->pm->width) + v->pm->width = strlen(val); } break; case PM_ARRAY: @@ -1514,31 +1767,24 @@ setstrvalue(Value v, char *val) setarrvalue(v, ss); } break; + case PM_HASHED: + { + foundparam->gsu.s->setfn(foundparam, val); + } + break; } if ((!v->pm->env && !(v->pm->flags & PM_EXPORTED) && - !(isset(ALLEXPORT) && !v->pm->old)) || + !(isset(ALLEXPORT) && !(v->pm->flags & PM_HASHELEM))) || (v->pm->flags & PM_ARRAY) || v->pm->ename) return; - if (PM_TYPE(v->pm->flags) == PM_INTEGER) - convbase(val = buf, v->pm->gets.ifn(v->pm), v->pm->ct); - else if (v->pm->flags & (PM_EFLOAT|PM_FFLOAT)) - val = convfloat(v->pm->gets.ffn(v->pm), v->pm->ct, - v->pm->flags, NULL); - else - val = v->pm->gets.cfn(v->pm); - if (v->pm->env) - v->pm->env = replenv(v->pm->env, val, v->pm->flags); - else { - v->pm->flags |= PM_EXPORTED; - v->pm->env = addenv(v->pm->nam, val, v->pm->flags); - } + export_param(v->pm); } /**/ void setnumvalue(Value v, mnumber val) { - char buf[DIGBUFSIZE], *p; + char buf[BDIGBUFSIZE], *p; if (v->pm->flags & PM_READONLY) { zerr("read-only variable: %s", v->pm->nam, 0); @@ -1551,21 +1797,23 @@ setnumvalue(Value v, mnumber val) switch (PM_TYPE(v->pm->flags)) { case PM_SCALAR: case PM_ARRAY: - if (val.type & MN_INTEGER) - convbase(p = buf, val.u.l, 0); - else + if ((val.type & MN_INTEGER) || outputradix) { + if (!(val.type & MN_INTEGER)) + val.u.l = (zlong) val.u.d; + convbase(p = buf, val.u.l, outputradix); + } else p = convfloat(val.u.d, 0, 0, NULL); setstrvalue(v, ztrdup(p)); break; case PM_INTEGER: - (v->pm->sets.ifn) (v->pm, (val.type & MN_INTEGER) ? val.u.l : - (zlong) val.u.d); + v->pm->gsu.i->setfn(v->pm, (val.type & MN_INTEGER) ? val.u.l : + (zlong) val.u.d); setstrvalue(v, NULL); break; case PM_EFLOAT: case PM_FFLOAT: - (v->pm->sets.ffn) (v->pm, (val.type & MN_INTEGER) ? - (double)val.u.l : val.u.d); + v->pm->gsu.f->setfn(v->pm, (val.type & MN_INTEGER) ? + (double)val.u.l : val.u.d); setstrvalue(v, NULL); break; } @@ -1587,52 +1835,64 @@ setarrvalue(Value v, char **val) } if (!(PM_TYPE(v->pm->flags) & (PM_ARRAY|PM_HASHED))) { freearray(val); - zerr("attempt to assign array value to non-array", NULL, 0); + zerr("%s: attempt to assign array value to non-array", + v->pm->nam, 0); return; } - if (v->a == 0 && v->b == -1) { + if (v->start == 0 && v->end == -1) { if (PM_TYPE(v->pm->flags) == PM_HASHED) - arrhashsetfn(v->pm, val); + arrhashsetfn(v->pm, val, 0); else - (v->pm->sets.afn) (v->pm, val); + v->pm->gsu.a->setfn(v->pm, val); + } else if (v->start == -1 && v->end == 0 && + PM_TYPE(v->pm->flags) == PM_HASHED) { + arrhashsetfn(v->pm, val, 1); } else { char **old, **new, **p, **q, **r; int n, ll, i; if ((PM_TYPE(v->pm->flags) == PM_HASHED)) { freearray(val); - zerr("attempt to set slice of associative array", NULL, 0); + zerr("%s: attempt to set slice of associative array", + v->pm->nam, 0); return; } - if (v->inv && unset(KSHARRAYS)) - v->a--, v->b--; - q = old = v->pm->gets.afn(v->pm); + if (v->inv && unset(KSHARRAYS)) { + if (v->start > 0) + v->start--; + v->end--; + } + if (v->end < v->start) + v->end = v->start; + q = old = v->pm->gsu.a->getfn(v->pm); n = arrlen(old); - if (v->a < 0) - v->a += n; - if (v->b < 0) - v->b += n; - if (v->a < 0) - v->a = 0; - if (v->b < 0) - v->b = 0; - - ll = v->a + arrlen(val); - if (v->b < n) - ll += n - v->b; - - p = new = (char **) zcalloc(sizeof(char *) * (ll + 1)); - - for (i = 0; i < v->a; i++) + if (v->start < 0) { + v->start += n; + if (v->start < 0) + v->start = 0; + } + if (v->end < 0) { + v->end += n + 1; + if (v->end < 0) + v->end = 0; + } + + ll = v->start + arrlen(val); + if (v->end <= n) + ll += n - v->end + 1; + + p = new = (char **) zshcalloc(sizeof(char *) * (ll + 1)); + + for (i = 0; i < v->start; i++) *p++ = i < n ? ztrdup(*q++) : ztrdup(""); for (r = val; *r;) *p++ = ztrdup(*r++); - if (v->b + 1 < n) - for (q = old + v->b + 1; *q;) + if (v->end < n) + for (q = old + v->end; *q;) *p++ = ztrdup(*q++); *p = NULL; - (v->pm->sets.afn) (v->pm, new); + v->pm->gsu.a->setfn(v->pm, new); freearray(val); } } @@ -1694,7 +1954,7 @@ getaparam(char *s) if (!idigit(*s) && (v = getvalue(&vbuf, &s, 0)) && PM_TYPE(v->pm->flags) == PM_ARRAY) - return v->pm->gets.afn(v->pm); + return v->pm->gsu.a->getfn(v->pm); return NULL; } @@ -1709,18 +1969,36 @@ gethparam(char *s) if (!idigit(*s) && (v = getvalue(&vbuf, &s, 0)) && PM_TYPE(v->pm->flags) == PM_HASHED) - return paramvalarr(v->pm->gets.hfn(v->pm), SCANPM_WANTVALS); + return paramvalarr(v->pm->gsu.h->getfn(v->pm), SCANPM_WANTVALS); + return NULL; +} + +/* Retrieve the keys of an assoc array parameter as an array */ + +/**/ +mod_export char ** +gethkparam(char *s) +{ + struct value vbuf; + Value v; + + if (!idigit(*s) && (v = getvalue(&vbuf, &s, 0)) && + PM_TYPE(v->pm->flags) == PM_HASHED) + return paramvalarr(v->pm->gsu.h->getfn(v->pm), SCANPM_WANTKEYS); return NULL; } /**/ mod_export Param -setsparam(char *s, char *val) +assignsparam(char *s, char *val, int augment) { struct value vbuf; Value v; char *t = s; - char *ss; + char *ss, *copy, *var; + size_t lvar; + mnumber lhs, rhs; + int sstart; if (!isident(s)) { zerr("not an identifier: %s", s, 0); @@ -1728,6 +2006,7 @@ setsparam(char *s, char *val) errflag = 1; return NULL; } + queue_signals(); if ((ss = strchr(s, '['))) { *ss = '\0'; if (!(v = getvalue(&vbuf, &s, 1))) @@ -1737,24 +2016,100 @@ setsparam(char *s, char *val) } else { if (!(v = getvalue(&vbuf, &s, 1))) createparam(t, PM_SCALAR); - else if ((PM_TYPE(v->pm->flags) & (PM_ARRAY|PM_HASHED)) && - !(v->pm->flags & (PM_SPECIAL|PM_TIED)) && unset(KSHARRAYS)) { + else if ((((v->pm->flags & PM_ARRAY) && !augment) || + (v->pm->flags & PM_HASHED)) && + !(v->pm->flags & (PM_SPECIAL|PM_TIED)) && + unset(KSHARRAYS)) { unsetparam(t); createparam(t, PM_SCALAR); v = NULL; } } if (!v && !(v = getvalue(&vbuf, &t, 1))) { + unqueue_signals(); zsfree(val); return NULL; } + if (augment) { + if (v->start == 0 && v->end == -1) { + switch (PM_TYPE(v->pm->flags)) { + case PM_SCALAR: + v->start = INT_MAX; /* just append to scalar value */ + break; + case PM_INTEGER: + case PM_EFLOAT: + case PM_FFLOAT: + rhs = matheval(val); + lhs = getnumvalue(v); + if (lhs.type == MN_FLOAT) { + if ((rhs.type) == MN_FLOAT) + lhs.u.d = lhs.u.d + rhs.u.d; + else + lhs.u.d = lhs.u.d + (double)rhs.u.l; + } else { + if ((rhs.type) == MN_INTEGER) + lhs.u.l = lhs.u.l + rhs.u.l; + else + lhs.u.l = lhs.u.l + (zlong)rhs.u.d; + } + setnumvalue(v, lhs); + unqueue_signals(); + zsfree(val); + return v->pm; /* avoid later setstrvalue() call */ + case PM_ARRAY: + if (unset(KSHARRAYS)) { + v->start = arrlen(v->pm->gsu.a->getfn(v->pm)); + v->end = v->start + 1; + } else { + /* ksh appends scalar to first element */ + v->end = 1; + goto kshappend; + } + break; + } + } else { + switch (PM_TYPE(v->pm->flags)) { + case PM_SCALAR: + if (v->end > 0) + v->start = v->end; + else + v->start = v->end = strlen(v->pm->gsu.s->getfn(v->pm)) + + v->end + 1; + break; + case PM_INTEGER: + case PM_EFLOAT: + case PM_FFLOAT: + unqueue_signals(); + zerr("attempt to add to slice of a numeric variable", + NULL, 0); + zsfree(val); + return NULL; + case PM_ARRAY: + kshappend: + /* treat slice as the end element */ + v->start = sstart = v->end > 0 ? v->end - 1 : v->end; + v->isarr = 0; + var = getstrvalue(v); + v->start = sstart; + copy = val; + lvar = strlen(var); + val = (char *)zalloc(lvar + strlen(val) + 1); + strcpy(val, var); + strcpy(val + lvar, copy); + zsfree(copy); + break; + } + } + } + setstrvalue(v, val); + unqueue_signals(); return v->pm; } /**/ mod_export Param -setaparam(char *s, char **val) +assignaparam(char *s, char **val, int augment) { struct value vbuf; Value v; @@ -1767,13 +2122,16 @@ setaparam(char *s, char **val) errflag = 1; return NULL; } + queue_signals(); if ((ss = strchr(s, '['))) { *ss = '\0'; if (!(v = getvalue(&vbuf, &s, 1))) createparam(t, PM_ARRAY); *ss = '['; if (v && PM_TYPE(v->pm->flags) == PM_HASHED) { - zerr("attempt to set slice of associative array", NULL, 0); + unqueue_signals(); + zerr("%s: attempt to set slice of associative array", + v->pm->nam, 0); freearray(val); errflag = 1; return NULL; @@ -1785,15 +2143,49 @@ setaparam(char *s, char **val) else if (!(PM_TYPE(v->pm->flags) & (PM_ARRAY|PM_HASHED)) && !(v->pm->flags & (PM_SPECIAL|PM_TIED))) { int uniq = v->pm->flags & PM_UNIQUE; + if (augment) { + /* insert old value at the beginning of the val array */ + char **new; + int lv = arrlen(val); + + new = (char **) zalloc(sizeof(char *) * (lv + 2)); + *new = ztrdup(getstrvalue(v)); + memcpy(new+1, val, sizeof(char *) * (lv + 1)); + free(val); + val = new; + + } unsetparam(t); createparam(t, PM_ARRAY | uniq); v = NULL; } } if (!v) - if (!(v = fetchvalue(&vbuf, &t, 1, SCANPM_ASSIGNING))) + if (!(v = fetchvalue(&vbuf, &t, 1, SCANPM_ASSIGNING))) { + unqueue_signals(); + freearray(val); return NULL; + } + + if (augment) { + if (v->start == 0 && v->end == -1) { + if (PM_TYPE(v->pm->flags) & PM_ARRAY) { + v->start = arrlen(v->pm->gsu.a->getfn(v->pm)); + v->end = v->start + 1; + } else if (PM_TYPE(v->pm->flags) & PM_HASHED) + v->start = -1, v->end = 0; + } else { + if (v->end > 0) + v->start = v->end--; + else if (PM_TYPE(v->pm->flags) & PM_ARRAY) { + v->end = arrlen(v->pm->gsu.a->getfn(v->pm)) + v->end; + v->start = v->end + 1; + } + } + } + setarrvalue(v, val); + unqueue_signals(); return v->pm; } @@ -1816,20 +2208,23 @@ sethparam(char *s, char **val) zerr("nested associative arrays not yet supported", NULL, 0); errflag = 1; return NULL; - } else { - if (!(v = fetchvalue(&vbuf, &s, 1, SCANPM_ASSIGNING))) - createparam(t, PM_HASHED); - else if (!(PM_TYPE(v->pm->flags) & PM_HASHED) && - !(v->pm->flags & PM_SPECIAL)) { - unsetparam(t); - createparam(t, PM_HASHED); - v = NULL; - } + } + queue_signals(); + if (!(v = fetchvalue(&vbuf, &s, 1, SCANPM_ASSIGNING))) + createparam(t, PM_HASHED); + else if (!(PM_TYPE(v->pm->flags) & PM_HASHED) && + !(v->pm->flags & PM_SPECIAL)) { + unsetparam(t); + createparam(t, PM_HASHED); + v = NULL; } if (!v) - if (!(v = fetchvalue(&vbuf, &t, 1, SCANPM_ASSIGNING))) + if (!(v = fetchvalue(&vbuf, &t, 1, SCANPM_ASSIGNING))) { + unqueue_signals(); return NULL; + } setarrvalue(v, val); + unqueue_signals(); return v->pm; } @@ -1839,7 +2234,7 @@ setiparam(char *s, zlong val) { struct value vbuf; Value v; - char *t = s; + char *t = s, *ss; Param pm; mnumber mnval; @@ -1848,15 +2243,25 @@ setiparam(char *s, zlong val) errflag = 1; return NULL; } + queue_signals(); if (!(v = getvalue(&vbuf, &s, 1))) { - pm = createparam(t, PM_INTEGER); + if ((ss = strchr(s, '['))) + *ss = '\0'; + if (!(pm = createparam(t, ss ? PM_ARRAY : PM_INTEGER))) + pm = (Param) paramtab->getnode(paramtab, t); DPUTS(!pm, "BUG: parameter not created"); - pm->u.val = val; - return pm; + if (ss) { + *ss = '['; + } else { + pm->base = outputradix; + } + v = getvalue(&vbuf, &t, 1); + DPUTS(!v, "BUG: value not found for new parameter"); } mnval.type = MN_INTEGER; mnval.u.l = val; setnumvalue(v, mnval); + unqueue_signals(); return v->pm; } @@ -1871,7 +2276,7 @@ setnparam(char *s, mnumber val) { struct value vbuf; Value v; - char *t = s; + char *t = s, *ss = NULL; Param pm; if (!isident(s)) { @@ -1879,17 +2284,25 @@ setnparam(char *s, mnumber val) errflag = 1; return NULL; } + queue_signals(); if (!(v = getvalue(&vbuf, &s, 1))) { - pm = createparam(t, (val.type & MN_INTEGER) ? PM_INTEGER - : PM_FFLOAT); + if ((ss = strchr(s, '['))) + *ss = '\0'; + pm = createparam(t, ss ? PM_ARRAY : + (val.type & MN_INTEGER) ? PM_INTEGER : PM_FFLOAT); + if (!pm) + pm = (Param) paramtab->getnode(paramtab, t); DPUTS(!pm, "BUG: parameter not created"); - if (val.type & MN_INTEGER) - pm->u.val = val.u.l; - else - pm->u.dval = val.u.d; - return pm; + if (ss) { + *ss = '['; + } else if (val.type & MN_INTEGER) { + pm->base = outputradix; + } + v = getvalue(&vbuf, &t, 1); + DPUTS(!v, "BUG: value not found for new parameter"); } setnumvalue(v, val); + unqueue_signals(); return v->pm; } @@ -1901,40 +2314,52 @@ unsetparam(char *s) { Param pm; + queue_signals(); if ((pm = (Param) (paramtab == realparamtab ? gethashnode2(paramtab, s) : paramtab->getnode(paramtab, s)))) unsetparam_pm(pm, 0, 1); + unqueue_signals(); } /* Unset a parameter */ /**/ -mod_export void +mod_export int unsetparam_pm(Param pm, int altflag, int exp) { Param oldpm, altpm; if ((pm->flags & PM_READONLY) && pm->level <= locallevel) { zerr("read-only variable: %s", pm->nam, 0); - return; + return 1; } if ((pm->flags & PM_RESTRICTED) && isset(RESTRICTED)) { zerr("%s: restricted", pm->nam, 0); - return; - } - pm->unsetfn(pm, exp); - if ((pm->flags & PM_EXPORTED) && pm->env) { - delenv(pm->env); - zsfree(pm->env); - pm->env = NULL; + return 1; } + pm->gsu.s->unsetfn(pm, exp); + if (pm->env) + delenv(pm); /* remove it under its alternate name if necessary */ if (pm->ename && !altflag) { altpm = (Param) paramtab->getnode(paramtab, pm->ename); - if (altpm) + /* tied parameters are at the same local level as each other */ + oldpm = NULL; + while (altpm && altpm->level > pm->level) { + /* param under alternate name hidden by a local */ + oldpm = altpm; + altpm = altpm->old; + } + if (altpm) { + if (oldpm && !altpm->level) { + oldpm->old = NULL; + /* fudge things so removenode isn't called */ + altpm->level = 1; + } unsetparam_pm(altpm, 1, exp); + } } /* @@ -1951,7 +2376,7 @@ unsetparam_pm(Param pm, int altflag, int exp) */ if ((pm->level && locallevel >= pm->level) || (pm->flags & (PM_SPECIAL|PM_REMOVABLE)) == PM_SPECIAL) - return; + return 0; /* remove parameter node from table */ paramtab->removenode(paramtab, pm->nam); @@ -1960,24 +2385,45 @@ unsetparam_pm(Param pm, int altflag, int exp) oldpm = pm->old; paramtab->addnode(paramtab, oldpm->nam, oldpm); if ((PM_TYPE(oldpm->flags) == PM_SCALAR) && - oldpm->sets.cfn == strsetfn) + !(pm->flags & PM_HASHELEM) && + (oldpm->flags & PM_NAMEDDIR) && + oldpm->gsu.s == &stdscalar_gsu) adduserdir(oldpm->nam, oldpm->u.str, 0, 0); + if (oldpm->flags & PM_EXPORTED) { + /* + * Re-export the old value which we removed in typeset_single(). + * I don't think we need to test for ALL_EXPORT here, since if + * it was used to export the parameter originally the parameter + * should still have the PM_EXPORTED flag. + */ + export_param(oldpm); + } } paramtab->freenode((HashNode) pm); /* free parameter node */ + + return 0; } /* Standard function to unset a parameter. This is mostly delegated to * - * the specific set function. */ + * the specific set function. + * + * This could usefully be made type-specific, but then we need + * to be more careful when calling the unset method directly. + */ /**/ mod_export void -stdunsetfn(Param pm, int exp) +stdunsetfn(Param pm, UNUSED(int exp)) { switch (PM_TYPE(pm->flags)) { - case PM_SCALAR: pm->sets.cfn(pm, NULL); break; - case PM_ARRAY: pm->sets.afn(pm, NULL); break; - case PM_HASHED: pm->sets.hfn(pm, NULL); break; + case PM_SCALAR: pm->gsu.s->setfn(pm, NULL); break; + case PM_ARRAY: pm->gsu.a->setfn(pm, NULL); break; + case PM_HASHED: pm->gsu.h->setfn(pm, NULL); break; + default: + if (!(pm->flags & PM_SPECIAL)) + pm->u.str = NULL; + break; } pm->flags |= PM_UNSET; } @@ -1985,7 +2431,7 @@ stdunsetfn(Param pm, int exp) /* Function to get value of an integer parameter */ /**/ -static zlong +mod_export zlong intgetfn(Param pm) { return pm->u.val; @@ -2035,17 +2481,21 @@ strsetfn(Param pm, char *x) { zsfree(pm->u.str); pm->u.str = x; - adduserdir(pm->nam, x, 0, 0); + if (!(pm->flags & PM_HASHELEM) && + ((pm->flags & PM_NAMEDDIR) || isset(AUTONAMEDIRS))) { + pm->flags |= PM_NAMEDDIR; + adduserdir(pm->nam, x, 0, 0); + } } /* Function to get value of an array parameter */ +static char *nullarray = NULL; + /**/ char ** arrgetfn(Param pm) { - static char *nullarray = NULL; - return pm->u.arr ? pm->u.arr : &nullarray; } @@ -2085,20 +2535,29 @@ hashsetfn(Param pm, HashTable x) pm->u.hash = x; } +/* Function to dispose of setting of an unsettable hash */ + +/**/ +mod_export void +nullsethashfn(Param pm, HashTable x) +{ + deleteparamtable(x); +} + /* Function to set value of an association parameter using key/value pairs */ /**/ static void -arrhashsetfn(Param pm, char **val) +arrhashsetfn(Param pm, char **val, int augment) { /* Best not to shortcut this by using the existing hash table, * * since that could cause trouble for special hashes. This way, * - * it's up to pm->sets.hfn() what to do. */ + * it's up to pm->gsu.h->setfn() what to do. */ int alen = arrlen(val); HashTable opmtab = paramtab, ht = 0; char **aptr = val; Value v = (Value) hcalloc(sizeof *v); - v->b = -1; + v->end = -1; if (alen % 2) { freearray(val); @@ -2107,7 +2566,8 @@ arrhashsetfn(Param pm, char **val) return; } if (alen) - ht = paramtab = newparamtable(17, pm->nam); + if (!(augment && (ht = paramtab = pm->gsu.h->getfn(pm)))) + ht = paramtab = newparamtable(17, pm->nam); while (*aptr) { /* The parameter name is ztrdup'd... */ v->pm = createparam(*aptr, PM_SCALAR|PM_UNSET); @@ -2122,20 +2582,34 @@ arrhashsetfn(Param pm, char **val) setstrvalue(v, *aptr++); } paramtab = opmtab; - pm->sets.hfn(pm, ht); + pm->gsu.h->setfn(pm, ht); free(val); /* not freearray() */ } -/* This function is used as the set function for * - * special parameters that cannot be set by the user. */ +/* + * These functions are used as the set function for special parameters that + * cannot be set by the user. The set is incomplete as the only such + * parameters are scalar and integer. + */ /**/ void -nullsetfn(Param pm, char *x) +nullstrsetfn(UNUSED(Param pm), char *x) { zsfree(x); } +/**/ +void +nullintsetfn(UNUSED(Param pm), UNUSED(zlong x)) +{} + +/**/ +mod_export void +nullunsetfn(UNUSED(Param pm), UNUSED(int exp)) +{} + + /* Function to get value of generic special integer * * parameter. data is pointer to global variable * * containing the integer value. */ @@ -2144,7 +2618,7 @@ nullsetfn(Param pm, char *x) mod_export zlong intvargetfn(Param pm) { - return *((zlong *)pm->u.data); + return *pm->u.valptr; } /* Function to set value of generic special integer * @@ -2155,7 +2629,7 @@ intvargetfn(Param pm) mod_export void intvarsetfn(Param pm, zlong x) { - *((zlong *)pm->u.data) = x; + *pm->u.valptr = x; } /* Function to set value of any ZLE-related integer * @@ -2166,7 +2640,7 @@ intvarsetfn(Param pm, zlong x) void zlevarsetfn(Param pm, zlong x) { - zlong *p = (zlong *)pm->u.data; + zlong *p = pm->u.valptr; *p = x; if (p == &lines || p == &columns) @@ -2211,7 +2685,9 @@ strvargetfn(Param pm) mod_export char ** arrvargetfn(Param pm) { - return *((char ***)pm->u.data); + char **arrptr = *((char ***)pm->u.data); + + return arrptr ? arrptr : &nullarray; } /* Function to set value of generic special array parameter. * @@ -2230,7 +2706,15 @@ arrvarsetfn(Param pm, char **x) freearray(*dptr); if (pm->flags & PM_UNIQUE) uniqarray(x); - *dptr = x ? x : mkarray(NULL); + /* + * Special tied arrays point to variables accessible in other + * ways which need to be set to NULL. We can't do this + * with user tied variables since we can leak memory. + */ + if ((pm->flags & PM_SPECIAL) && !x) + *dptr = mkarray(NULL); + else + *dptr = x; if (pm->ename && x) arrfixenv(pm->ename, x); } @@ -2248,46 +2732,116 @@ void colonarrsetfn(Param pm, char *x) { char ***dptr = (char ***)pm->u.data; - /* - * If this is tied to a parameter (rather than internal) array, - * the array itself may be NULL. Otherwise, we have to make - * sure it doesn't ever get null. + * We have to make sure this is never NULL, since that + * can cause problems. */ if (*dptr) freearray(*dptr); - *dptr = x ? colonsplit(x, pm->flags & PM_UNIQUE) : - (pm->flags & PM_TIED) ? NULL : mkarray(NULL); + if (x) + *dptr = colonsplit(x, pm->flags & PM_UNIQUE); + else + *dptr = mkarray(NULL); if (pm->ename) arrfixenv(pm->nam, *dptr); zsfree(x); } /**/ -int +char * +tiedarrgetfn(Param pm) +{ + struct tieddata *dptr = (struct tieddata *)pm->u.data; + return *dptr->arrptr ? zjoin(*dptr->arrptr, dptr->joinchar, 1) : ""; +} + +/**/ +void +tiedarrsetfn(Param pm, char *x) +{ + struct tieddata *dptr = (struct tieddata *)pm->u.data; + + if (*dptr->arrptr) + freearray(*dptr->arrptr); + if (x) { + char sepbuf[3]; + if (imeta(dptr->joinchar)) + { + sepbuf[0] = Meta; + sepbuf[1] = dptr->joinchar ^ 32; + sepbuf[2] = '\0'; + } + else + { + sepbuf[0] = dptr->joinchar; + sepbuf[1] = '\0'; + } + *dptr->arrptr = sepsplit(x, sepbuf, 0, 0); + if (pm->flags & PM_UNIQUE) + uniqarray(*dptr->arrptr); + } else + *dptr->arrptr = NULL; + if (pm->ename) + arrfixenv(pm->nam, *dptr->arrptr); + zsfree(x); +} + +/**/ +void +tiedarrunsetfn(Param pm, UNUSED(int exp)) +{ + /* + * Special unset function because we allocated a struct tieddata + * in typeset_single to hold the special data which we now + * need to delete. + */ + pm->gsu.s->setfn(pm, NULL); + zfree(pm->u.data, sizeof(struct tieddata)); + /* paranoia -- shouldn't need these, but in case we reuse the struct... */ + pm->u.data = NULL; + zsfree(pm->ename); + pm->ename = NULL; + pm->flags &= ~PM_TIED; +} + +/**/ +void uniqarray(char **x) { - int changes = 0; char **t, **p = x; if (!x || !*x) - return 0; + return; while (*++p) for (t = x; t < p; t++) if (!strcmp(*p, *t)) { zsfree(*p); for (t = p--; (*t = t[1]) != NULL; t++); - changes++; break; } - return changes; +} + +/**/ +void +zhuniqarray(char **x) +{ + char **t, **p = x; + + if (!x || !*x) + return; + while (*++p) + for (t = x; t < p; t++) + if (!strcmp(*p, *t)) { + for (t = p--; (*t = t[1]) != NULL; t++); + break; + } } /* Function to get value of special parameter `#' and `ARGC' */ /**/ zlong -poundgetfn(Param pm) +poundgetfn(UNUSED(Param pm)) { return arrlen(pparams); } @@ -2296,7 +2850,7 @@ poundgetfn(Param pm) /**/ zlong -randomgetfn(Param pm) +randomgetfn(UNUSED(Param pm)) { return rand() & 0x7fff; } @@ -2305,7 +2859,7 @@ randomgetfn(Param pm) /**/ void -randomsetfn(Param pm, zlong v) +randomsetfn(UNUSED(Param pm), zlong v) { srand((unsigned int)v); } @@ -2314,26 +2868,101 @@ randomsetfn(Param pm, zlong v) /**/ zlong -secondsgetfn(Param pm) +intsecondsgetfn(UNUSED(Param pm)) { - return time(NULL) - shtimer.tv_sec; + struct timeval now; + struct timezone dummy_tz; + + gettimeofday(&now, &dummy_tz); + + return (zlong)(now.tv_sec - shtimer.tv_sec) + + (zlong)(now.tv_usec - shtimer.tv_usec) / (zlong)1000000; } /* Function to set value of special parameter `SECONDS' */ /**/ void -secondssetfn(Param pm, zlong x) +intsecondssetfn(UNUSED(Param pm), zlong x) { - shtimer.tv_sec = time(NULL) - x; + struct timeval now; + struct timezone dummy_tz; + zlong diff; + + gettimeofday(&now, &dummy_tz); + diff = (zlong)now.tv_sec - x; + shtimer.tv_sec = diff; + if ((zlong)shtimer.tv_sec != diff) + zwarn("SECONDS truncated on assignment", NULL, 0); shtimer.tv_usec = 0; } +/**/ +double +floatsecondsgetfn(UNUSED(Param pm)) +{ + struct timeval now; + struct timezone dummy_tz; + + gettimeofday(&now, &dummy_tz); + + return (double)(now.tv_sec - shtimer.tv_sec) + + (double)(now.tv_usec - shtimer.tv_usec) / 1000000.0; +} + +/**/ +void +floatsecondssetfn(UNUSED(Param pm), double x) +{ + struct timeval now; + struct timezone dummy_tz; + + gettimeofday(&now, &dummy_tz); + shtimer.tv_sec = now.tv_sec - (zlong)x; + shtimer.tv_usec = now.tv_usec - (zlong)((x - (zlong)x) * 1000000.0); +} + +/**/ +double +getrawseconds(void) +{ + return (double)shtimer.tv_sec + (double)shtimer.tv_usec / 1000000.0; +} + +/**/ +void +setrawseconds(double x) +{ + shtimer.tv_sec = (zlong)x; + shtimer.tv_usec = (zlong)((x - (zlong)x) * 1000000.0); +} + +/**/ +int +setsecondstype(Param pm, int on, int off) +{ + int newflags = (pm->flags | on) & ~off; + int tp = PM_TYPE(newflags); + /* Only one of the numeric types is allowed. */ + if (tp == PM_EFLOAT || tp == PM_FFLOAT) + { + pm->gsu.f = &floatseconds_gsu; + } + else if (tp == PM_INTEGER) + { + pm->gsu.i = &intseconds_gsu; + } + else + return 1; + pm->flags = newflags; + return 0; +} + /* Function to get value for special parameter `USERNAME' */ /**/ char * -usernamegetfn(Param pm) +usernamegetfn(UNUSED(Param pm)) { return get_username(); } @@ -2342,7 +2971,7 @@ usernamegetfn(Param pm) /**/ void -usernamesetfn(Param pm, char *x) +usernamesetfn(UNUSED(Param pm), char *x) { #if defined(HAVE_SETUID) && defined(HAVE_GETPWNAM) struct passwd *pswd; @@ -2358,13 +2987,14 @@ usernamesetfn(Param pm, char *x) } } #endif /* HAVE_SETUID && HAVE_GETPWNAM */ + zsfree(x); } /* Function to get value for special parameter `UID' */ /**/ zlong -uidgetfn(Param pm) +uidgetfn(UNUSED(Param pm)) { return getuid(); } @@ -2373,10 +3003,10 @@ uidgetfn(Param pm) /**/ void -uidsetfn(Param pm, uid_t x) +uidsetfn(UNUSED(Param pm), zlong x) { #ifdef HAVE_SETUID - setuid(x); + setuid((uid_t)x); #endif } @@ -2384,7 +3014,7 @@ uidsetfn(Param pm, uid_t x) /**/ zlong -euidgetfn(Param pm) +euidgetfn(UNUSED(Param pm)) { return geteuid(); } @@ -2393,10 +3023,10 @@ euidgetfn(Param pm) /**/ void -euidsetfn(Param pm, uid_t x) +euidsetfn(UNUSED(Param pm), zlong x) { #ifdef HAVE_SETEUID - seteuid(x); + seteuid((uid_t)x); #endif } @@ -2404,7 +3034,7 @@ euidsetfn(Param pm, uid_t x) /**/ zlong -gidgetfn(Param pm) +gidgetfn(UNUSED(Param pm)) { return getgid(); } @@ -2413,10 +3043,10 @@ gidgetfn(Param pm) /**/ void -gidsetfn(Param pm, gid_t x) +gidsetfn(UNUSED(Param pm), zlong x) { #ifdef HAVE_SETUID - setgid(x); + setgid((gid_t)x); #endif } @@ -2424,7 +3054,7 @@ gidsetfn(Param pm, gid_t x) /**/ zlong -egidgetfn(Param pm) +egidgetfn(UNUSED(Param pm)) { return getegid(); } @@ -2433,16 +3063,16 @@ egidgetfn(Param pm) /**/ void -egidsetfn(Param pm, gid_t x) +egidsetfn(UNUSED(Param pm), zlong x) { #ifdef HAVE_SETEUID - setegid(x); + setegid((gid_t)x); #endif } /**/ zlong -ttyidlegetfn(Param pm) +ttyidlegetfn(UNUSED(Param pm)) { struct stat ttystat; @@ -2455,7 +3085,7 @@ ttyidlegetfn(Param pm) /**/ char * -ifsgetfn(Param pm) +ifsgetfn(UNUSED(Param pm)) { return ifs; } @@ -2464,7 +3094,7 @@ ifsgetfn(Param pm) /**/ void -ifssetfn(Param pm, char *x) +ifssetfn(UNUSED(Param pm), char *x) { zsfree(ifs); ifs = x; @@ -2503,9 +3133,11 @@ setlang(char *x) struct localename *ln; setlocale(LC_ALL, x ? x : ""); + queue_signals(); for (ln = lc_names; ln->name; ln++) if ((x = getsparam(ln->name))) setlocale(ln->category, x); + unqueue_signals(); } /**/ @@ -2513,8 +3145,11 @@ void lc_allsetfn(Param pm, char *x) { strsetfn(pm, x); - if (!x) + if (!x) { + queue_signals(); setlang(getsparam("LANG")); + unqueue_signals(); + } else setlocale(LC_ALL, x); } @@ -2536,12 +3171,14 @@ lcsetfn(Param pm, char *x) strsetfn(pm, x); if (getsparam("LC_ALL")) return; + queue_signals(); if (!x) x = getsparam("LANG"); for (ln = lc_names; ln->name; ln++) if (!strcmp(ln->name, pm->nam)) setlocale(ln->category, x ? x : ""); + unqueue_signals(); } #endif /* USE_LOCALE */ @@ -2549,7 +3186,7 @@ lcsetfn(Param pm, char *x) /**/ zlong -histsizegetfn(Param pm) +histsizegetfn(UNUSED(Param pm)) { return histsiz; } @@ -2558,18 +3195,48 @@ histsizegetfn(Param pm) /**/ void -histsizesetfn(Param pm, zlong v) +histsizesetfn(UNUSED(Param pm), zlong v) { - if ((histsiz = v) <= 2) - histsiz = 2; + if ((histsiz = v) < 1) + histsiz = 1; resizehistents(); } +/* Function to get value for special parameter `SAVEHIST' */ + +/**/ +zlong +savehistsizegetfn(UNUSED(Param pm)) +{ + return savehistsiz; +} + +/* Function to set value of special parameter `SAVEHIST' */ + +/**/ +void +savehistsizesetfn(UNUSED(Param pm), zlong v) +{ + if ((savehistsiz = v) < 0) + savehistsiz = 0; +} + +/* Function to set value for special parameter `ERRNO' */ + +/**/ +void +errnosetfn(UNUSED(Param pm), zlong x) +{ + errno = (int)x; + if ((zlong)errno != x) + zwarn("errno truncated on assignment", NULL, 0); +} + /* Function to get value for special parameter `ERRNO' */ /**/ zlong -errnogetfn(Param pm) +errnogetfn(UNUSED(Param pm)) { return errno; } @@ -2578,7 +3245,7 @@ errnogetfn(Param pm) /**/ char * -histcharsgetfn(Param pm) +histcharsgetfn(UNUSED(Param pm)) { static char buf[4]; @@ -2593,7 +3260,7 @@ histcharsgetfn(Param pm) /**/ void -histcharssetfn(Param pm, char *x) +histcharssetfn(UNUSED(Param pm), char *x) { if (x) { bangchar = x[0]; @@ -2612,7 +3279,7 @@ histcharssetfn(Param pm, char *x) /**/ char * -homegetfn(Param pm) +homegetfn(UNUSED(Param pm)) { return home; } @@ -2621,7 +3288,7 @@ homegetfn(Param pm) /**/ void -homesetfn(Param pm, char *x) +homesetfn(UNUSED(Param pm), char *x) { zsfree(home); if (x && isset(CHASELINKS) && (home = xsymlink(x))) @@ -2635,7 +3302,7 @@ homesetfn(Param pm, char *x) /**/ char * -wordcharsgetfn(Param pm) +wordcharsgetfn(UNUSED(Param pm)) { return wordchars; } @@ -2644,7 +3311,7 @@ wordcharsgetfn(Param pm) /**/ void -wordcharssetfn(Param pm, char *x) +wordcharssetfn(UNUSED(Param pm), char *x) { zsfree(wordchars); wordchars = x; @@ -2655,7 +3322,7 @@ wordcharssetfn(Param pm, char *x) /**/ char * -underscoregetfn(Param pm) +underscoregetfn(UNUSED(Param pm)) { char *u = dupstring(underscore); @@ -2667,7 +3334,7 @@ underscoregetfn(Param pm) /**/ char * -termgetfn(Param pm) +termgetfn(UNUSED(Param pm)) { return term; } @@ -2676,7 +3343,7 @@ termgetfn(Param pm) /**/ void -termsetfn(Param pm, char *x) +termsetfn(UNUSED(Param pm), char *x) { zsfree(term); term = x ? x : ztrdup(""); @@ -2688,54 +3355,149 @@ termsetfn(Param pm, char *x) init_term(); } -/* We could probably replace the replenv with the actual code to * - * do the replacing, since we've already scanned for the string. */ +/* Function to get value for special parameter `pipestatus' */ + +/**/ +static char ** +pipestatgetfn(UNUSED(Param pm)) +{ + char **x = (char **) zhalloc((numpipestats + 1) * sizeof(char *)); + char buf[20], **p; + int *q, i; + + for (p = x, q = pipestats, i = numpipestats; i--; p++, q++) { + sprintf(buf, "%d", *q); + *p = dupstring(buf); + } + *p = NULL; + + return x; +} + +/* Function to get value for special parameter `pipestatus' */ /**/ static void +pipestatsetfn(UNUSED(Param pm), char **x) +{ + if (x) { + int i; + + for (i = 0; *x && i < MAX_PIPESTATS; i++, x++) + pipestats[i] = atoi(*x); + numpipestats = i; + } + else + numpipestats = 0; +} + +/**/ +void arrfixenv(char *s, char **t) { - char **ep, *u; - int len_s; Param pm; + int joinchar; + + if (t == path) + cmdnamtab->emptytable(cmdnamtab); 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) + + if (pm->flags & PM_HASHELEM) return; - u = t ? zjoin(t, ':', 1) : ""; - len_s = strlen(s); - for (ep = environ; *ep; ep++) - if (!strncmp(*ep, s, len_s) && (*ep)[len_s] == '=') { - pm->env = replenv(*ep, u, pm->flags); - return; - } + if (isset(ALLEXPORT)) pm->flags |= PM_EXPORTED; - if (pm->flags & PM_EXPORTED) - pm->env = addenv(s, u, pm->flags); + + /* + * Do not "fix" parameters that were not exported + */ + + if (!(pm->flags & PM_EXPORTED)) + return; + + if (pm->flags & PM_TIED) + joinchar = ((struct tieddata *)pm->u.data)->joinchar; + else + joinchar = ':'; + + addenv(pm, t ? zjoin(t, joinchar, 1) : ""); } -/* Given *name = "foo", it searchs the environment for string * - * "foo=bar", and returns a pointer to the beginning of "bar" */ + +/**/ +int +zputenv(char *str) +{ +#ifdef HAVE_PUTENV + return putenv(str); +#else + char **ep; + int num_env; + + + /* First check if there is already an environment * + * variable matching string `name'. */ + if (findenv(str, &num_env)) { + environ[num_env] = str; + } else { + /* Else we have to make room and add it */ + num_env = arrlen(environ); + environ = (char **) zrealloc(environ, (sizeof(char *)) * (num_env + 2)); + + /* Now add it at the end */ + ep = environ + num_env; + *ep = str; + *(ep + 1) = NULL; + } + return 0; +#endif +} + +/**/ +static int +findenv(char *name, int *pos) +{ + char **ep, *eq; + int nlen; + + + eq = strchr(name, '='); + nlen = eq ? eq - name : (int)strlen(name); + for (ep = environ; *ep; ep++) + if (!strncmp (*ep, name, nlen) && *((*ep)+nlen) == '=') { + if (pos) + *pos = ep - environ; + return 1; + } + + return 0; +} + +/* Given *name = "foo", it searches the environment for string * + * "foo=bar", and returns a pointer to the beginning of "bar" */ /**/ mod_export char * zgetenv(char *name) { +#ifdef HAVE_GETENV + return getenv(name); +#else char **ep, *s, *t; for (ep = environ; *ep; ep++) { - for (s = *ep, t = name; *s && *s == *t; s++, t++); - if (*s == '=' && !*t) - return s + 1; + for (s = *ep, t = name; *s && *s == *t; s++, t++); + if (*s == '=' && !*t) + return s + 1; } return NULL; +#endif } /**/ @@ -2752,29 +3514,47 @@ copyenvstr(char *s, char *value, int flags) } } -/* Change the value of an existing environment variable */ - /**/ -char * -replenv(char *e, char *value, int flags) -{ - char **ep, *s; - int len_value; - - for (ep = environ; *ep; ep++) - if (*ep == e) { - for (len_value = 0, s = value; - *s && (*s++ != Meta || *s++ != 32); len_value++); - s = e; - while (*s++ != '='); - *ep = (char *) zrealloc(e, s - e + len_value + 1); - s = s - e + *ep - 1; - copyenvstr(s, value, flags); - return *ep; - } - return NULL; +void +addenv(Param pm, char *value) +{ + char *oldenv = 0, *newenv = 0, *env = 0; + int pos; + + /* First check if there is already an environment * + * variable matching string `name'. If not, and * + * we are not requested to add new, return */ + if (findenv(pm->nam, &pos)) + oldenv = environ[pos]; + + newenv = mkenvstr(pm->nam, value, pm->flags); + if (zputenv(newenv)) { + zsfree(newenv); + pm->env = NULL; + return; + } + /* + * Under Cygwin we must use putenv() to maintain consistency. + * Unfortunately, current version (1.1.2) copies argument and may + * silently reuse existing environment string. This tries to + * check for both cases + */ + if (findenv(pm->nam, &pos)) { + env = environ[pos]; + if (env != oldenv) + zsfree(oldenv); + if (env != newenv) + zsfree(newenv); + pm->flags |= PM_EXPORTED; + pm->env = env; + return; + } + + DPUTS(1, "addenv should never reach the end"); + pm->env = NULL; } + /* Given strings *name = "foo", *value = "bar", * * return a new string *str = "foo=bar". */ @@ -2801,32 +3581,21 @@ mkenvstr(char *name, char *value, int flags) * pointer to the location of this new environment * * string. */ + /**/ -char * -addenv(char *name, char *value, int flags) +void +delenvvalue(char *x) { - char **ep, *s, *t; - int num_env; + char **ep; - /* First check if there is already an environment * - * variable matching string `name'. */ for (ep = environ; *ep; ep++) { - for (s = *ep, t = name; *s && *s == *t; s++, t++); - if (*s == '=' && !*t) { - zsfree(*ep); - return *ep = mkenvstr(name, value, flags); - } + if (*ep == x) + break; } - - /* Else we have to make room and add it */ - num_env = arrlen(environ); - environ = (char **) zrealloc(environ, (sizeof(char *)) * (num_env + 2)); - - /* Now add it at the end */ - ep = environ + num_env; - *ep = mkenvstr(name, value, flags); - *(ep + 1) = NULL; - return *ep; + if (*ep) { + for (; (ep[0] = ep[1]); ep++); + } + zsfree(x); } /* Delete a pointer from the list of pointers to environment * @@ -2834,16 +3603,15 @@ addenv(char *name, char *value, int flags) /**/ void -delenv(char *x) +delenv(Param pm) { - char **ep; - - for (ep = environ; *ep; ep++) { - if (*ep == x) - break; - } - if (*ep) - for (; (ep[0] = ep[1]); ep++); + delenvvalue(pm->env); + pm->env = NULL; + /* + * Note we don't remove PM_EXPORT from the flags. This + * may be asking for trouble but we need to know later + * if we restore this parameter to its old value. + */ } /**/ @@ -2855,13 +3623,21 @@ convbase(char *s, zlong v, int base) if (v < 0) *s++ = '-', v = -v; - if (base <= 1) - base = 10; - - if (base != 10) { - sprintf(s, "%d#", base); + if (base >= -1 && base <= 1) + base = -10; + + if (base > 0) { + if (isset(CBASES) && base == 16) + sprintf(s, "0x"); + else if (isset(CBASES) && base == 8 && isset(OCTALZEROES)) + sprintf(s, "0"); + else if (base != 10) + sprintf(s, "%d#", base); + else + *s = 0; s += strlen(s); - } + } else + base = -base; for (x = v; x; digs++) x /= base; if (!digs) @@ -2879,7 +3655,7 @@ convbase(char *s, zlong v, int base) /* * Convert a floating point value for output. * Unlike convbase(), this has its own internal storage and returns - * a value from the heap; + * a value from the heap. */ /**/ @@ -2887,6 +3663,7 @@ char * convfloat(double dval, int digits, int flags, FILE *fout) { char fmt[] = "%.*e"; + char *prev_locale, *ret; /* * The difficulty with the buffer size is that a %f conversion @@ -2921,14 +3698,24 @@ convfloat(double dval, int digits, int flags, FILE *fout) digits--; } } +#ifdef USE_LOCALE + prev_locale = dupstring(setlocale(LC_NUMERIC, NULL)); + setlocale(LC_NUMERIC, "POSIX"); +#endif if (fout) { fprintf(fout, fmt, digits, dval); - return NULL; + ret = NULL; } else { VARARR(char, buf, 512 + digits); sprintf(buf, fmt, digits, dval); - return dupstring(buf); + if (!strchr(buf, 'e') && !strchr(buf, '.')) + strcat(buf, "."); + ret = dupstring(buf); } +#ifdef USE_LOCALE + if (prev_locale) setlocale(LC_NUMERIC, prev_locale); +#endif + return ret; } /* Start a parameter scope */ @@ -2947,12 +3734,14 @@ mod_export void endparamscope(void) { locallevel--; + /* This pops anything from a higher locallevel */ + saveandpophiststack(0, HFILE_USE_OPTIONS); scanhashtable(paramtab, 0, 0, 0, scanendscope, 0); } /**/ static void -scanendscope(HashNode hn, int flags) +scanendscope(HashNode hn, UNUSED(int flags)) { Param pm = (Param)hn; if (pm->level > locallevel) { @@ -2967,35 +3756,50 @@ scanendscope(HashNode hn, int flags) */ Param tpm = pm->old; + if (!strcmp(pm->nam, "SECONDS")) + { + setsecondstype(pm, PM_TYPE(tpm->flags), PM_TYPE(pm->flags)); + /* + * We restore SECONDS by restoring its raw internal value + * that we cached off into tpm->u.dval. + */ + setrawseconds(tpm->u.dval); + tpm->flags |= PM_NORESTORE; + } 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; + pm->base = tpm->base; + pm->width = tpm->width; + if (pm->env) + delenv(pm); if (!(tpm->flags & PM_NORESTORE)) switch (PM_TYPE(pm->flags)) { case PM_SCALAR: - pm->sets.cfn(pm, tpm->u.str); + pm->gsu.s->setfn(pm, tpm->u.str); break; case PM_INTEGER: - pm->sets.ifn(pm, tpm->u.val); + pm->gsu.i->setfn(pm, tpm->u.val); break; case PM_EFLOAT: case PM_FFLOAT: - pm->sets.ffn(pm, tpm->u.dval); + pm->gsu.f->setfn(pm, tpm->u.dval); break; case PM_ARRAY: - pm->sets.afn(pm, tpm->u.arr); + pm->gsu.a->setfn(pm, tpm->u.arr); break; case PM_HASHED: - pm->sets.hfn(pm, tpm->u.hash); + pm->gsu.h->setfn(pm, tpm->u.hash); break; } zfree(tpm, sizeof(*tpm)); + + if (pm->flags & PM_EXPORTED) + export_param(pm); } else unsetparam_pm(pm, 0, 0); } @@ -3015,7 +3819,7 @@ freeparamnode(HashNode hn) /* Since the second flag to unsetfn isn't used, I don't * * know what its value should be. */ if (delunset) - pm->unsetfn(pm, 1); + pm->gsu.s->unsetfn(pm, 1); zsfree(pm->nam); /* If this variable was tied by the user, ename was ztrdup'd */ if (pm->flags & PM_TIED) @@ -3025,6 +3829,39 @@ freeparamnode(HashNode hn) /* Print a parameter */ +enum paramtypes_flags { + PMTF_USE_BASE = (1<<0), + PMTF_USE_WIDTH = (1<<1), + PMTF_TEST_LEVEL = (1<<2) +}; + +struct paramtypes { + int binflag; /* The relevant PM_FLAG(S) */ + const char *string; /* String for verbose output */ + int typeflag; /* Flag for typeset -? */ + int flags; /* The enum above */ +}; + +static const struct paramtypes pmtypes[] = { + { PM_AUTOLOAD, "undefined", 0, 0}, + { PM_INTEGER, "integer", 'i', PMTF_USE_BASE}, + { PM_EFLOAT, "float", 'E', 0}, + { PM_FFLOAT, "float", 'F', 0}, + { PM_ARRAY, "array", 'a', 0}, + { PM_HASHED, "association", 'A', 0}, + { 0, "local", 0, PMTF_TEST_LEVEL}, + { PM_LEFT, "left justified", 'L', PMTF_USE_WIDTH}, + { PM_RIGHT_B, "right justified", 'R', PMTF_USE_WIDTH}, + { PM_RIGHT_Z, "zero filled", 'Z', PMTF_USE_WIDTH}, + { PM_LOWER, "lowercase", 'l', 0}, + { PM_UPPER, "uppercase", 'u', 0}, + { PM_READONLY, "readonly", 'r', 0}, + { PM_TAGGED, "tagged", 't', 0}, + { PM_EXPORTED, "exported", 'x', 0} +}; + +#define PMTYPES_SIZE ((int)(sizeof(pmtypes)/sizeof(struct paramtypes))) + /**/ mod_export void printparamnode(HashNode hn, int printflags) @@ -3035,39 +3872,50 @@ printparamnode(HashNode hn, int printflags) if (p->flags & PM_UNSET) return; + if (printflags & PRINT_TYPESET) + printf("typeset "); + /* Print the attributes of the parameter */ - if (printflags & PRINT_TYPE) { - if (p->flags & PM_AUTOLOAD) - printf("undefined "); - if (p->flags & PM_INTEGER) - printf("integer "); - if (p->flags & (PM_EFLOAT|PM_FFLOAT)) - printf("float "); - else if (p->flags & PM_ARRAY) - printf("array "); - else if (p->flags & PM_HASHED) - printf("association "); - if (p->level) - printf("local "); - if (p->flags & PM_LEFT) - printf("left justified %d ", p->ct); - if (p->flags & PM_RIGHT_B) - printf("right justified %d ", p->ct); - if (p->flags & PM_RIGHT_Z) - printf("zero filled %d ", p->ct); - if (p->flags & PM_LOWER) - printf("lowercase "); - if (p->flags & PM_UPPER) - printf("uppercase "); - if (p->flags & PM_READONLY) - printf("readonly "); - if (p->flags & PM_TAGGED) - printf("tagged "); - if (p->flags & PM_EXPORTED) - printf("exported "); - } - - if (printflags & PRINT_NAMEONLY) { + if (printflags & (PRINT_TYPE|PRINT_TYPESET)) { + int doneminus = 0, i; + const struct paramtypes *pmptr; + + for (pmptr = pmtypes, i = 0; i < PMTYPES_SIZE; i++, pmptr++) { + int doprint = 0; + if (pmptr->flags & PMTF_TEST_LEVEL) { + if (p->level) + doprint = 1; + } else if (p->flags & pmptr->binflag) + doprint = 1; + + if (doprint) { + if (printflags & PRINT_TYPESET) { + if (pmptr->typeflag) { + if (!doneminus) { + putchar('-'); + doneminus = 1; + } + putchar(pmptr->typeflag); + } + } else { + printf("%s ", pmptr->string); + } + if ((pmptr->flags & PMTF_USE_BASE) && p->base) { + printf("%d ", p->base); + doneminus = 0; + } + if ((pmptr->flags & PMTF_USE_WIDTH) && p->width) { + printf("%d ", p->width); + doneminus = 0; + } + } + } + if (doneminus) + putchar(' '); + } + + if ((printflags & PRINT_NAMEONLY) || + ((p->flags & PM_HIDEVAL) && !(printflags & PRINT_INCLUDEVALUE))) { zputs(p->nam, stdout); putchar('\n'); return; @@ -3081,6 +3929,9 @@ printparamnode(HashNode hn, int printflags) } if (printflags & PRINT_KV_PAIR) putchar(' '); + else if ((printflags & PRINT_TYPESET) && + (PM_TYPE(p->flags) == PM_ARRAY || PM_TYPE(p->flags) == PM_HASHED)) + printf("\n%s=", p->nam); else putchar('='); @@ -3089,27 +3940,27 @@ printparamnode(HashNode hn, int printflags) switch (PM_TYPE(p->flags)) { case PM_SCALAR: /* string: simple output */ - if (p->gets.cfn && (t = p->gets.cfn(p))) + if (p->gsu.s->getfn && (t = p->gsu.s->getfn(p))) quotedzputs(t, stdout); break; case PM_INTEGER: /* integer */ #ifdef ZSH_64_BIT_TYPE - fputs(output64(p->gets.ifn(p)), stdout); + fputs(output64(p->gsu.i->getfn(p)), stdout); #else - printf("%ld", p->gets.ifn(p)); + printf("%ld", p->gsu.i->getfn(p)); #endif break; case PM_EFLOAT: case PM_FFLOAT: /* float */ - convfloat(p->gets.ffn(p), p->ct, p->flags, stdout); + convfloat(p->gsu.f->getfn(p), p->base, p->flags, stdout); break; case PM_ARRAY: /* array */ if (!(printflags & PRINT_KV_PAIR)) putchar('('); - u = p->gets.afn(p); + u = p->gsu.a->getfn(p); if(*u) { quotedzputs(*u++, stdout); while (*u) { @@ -3125,7 +3976,7 @@ printparamnode(HashNode hn, int printflags) if (!(printflags & PRINT_KV_PAIR)) putchar('('); { - HashTable ht = p->gets.hfn(p); + HashTable ht = p->gsu.h->getfn(p); if (ht) scanhashtable(ht, 0, 0, PM_UNSET, ht->printnode, PRINT_KV_PAIR); -- cgit 1.4.1