/*
 * parameter.c - parameter interface to zsh internals
 *
 * This file is part of zsh, the Z shell.
 *
 * Copyright (c) 1999 Sven Wischnowsky
 * All rights reserved.
 *
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and to distribute modified versions of this software for any
 * purpose, provided that the above copyright notice and the following
 * two paragraphs appear in all copies of this software.
 *
 * In no event shall Sven Wischnowsky or the Zsh Development Group be liable
 * to any party for direct, indirect, special, incidental, or consequential
 * damages arising out of the use of this software and its documentation,
 * even if Sven Wischnowsky and the Zsh Development Group have been advised of
 * the possibility of such damage.
 *
 * Sven Wischnowsky and the Zsh Development Group specifically disclaim any
 * warranties, including, but not limited to, the implied warranties of
 * merchantability and fitness for a particular purpose.  The software
 * provided hereunder is on an "as is" basis, and Sven Wischnowsky and the
 * Zsh Development Group have no obligation to provide maintenance,
 * support, updates, enhancements, or modifications.
 *
 */

#include "parameter.mdh"
#include "parameter.pro"

/* This says if we are cleaning up when the module is unloaded. */

static int incleanup;

/* Empty dummy function for special hash parameters. */

/**/
static void
shempty(void)
{
}

/* Create a simple special hash parameter. */

/**/
static Param
createspecialhash(char *name, GetNodeFunc get, ScanTabFunc scan)
{
    Param pm;
    HashTable ht;

    if (!(pm = createparam(name, PM_SPECIAL|PM_HIDE|PM_HIDEVAL|
			   PM_REMOVABLE|PM_HASHED)))
	return NULL;

    pm->level = pm->old ? locallevel : 0;
    pm->gsu.h = &stdhash_gsu;
    pm->u.hash = ht = newhashtable(0, name, NULL);

    ht->hash        = hasher;
    ht->emptytable  = (TableFunc) shempty;
    ht->filltable   = NULL;
    ht->addnode     = (AddNodeFunc) shempty;
    ht->getnode     = ht->getnode2 = get;
    ht->removenode  = (RemoveNodeFunc) shempty;
    ht->disablenode = NULL;
    ht->enablenode  = NULL;
    ht->freenode    = (FreeNodeFunc) shempty;
    ht->printnode   = printparamnode;
    ht->scantab     = scan;

    return pm;
}

/* Functions for the parameters special parameter. */

/* Return a string describing the type of a parameter. */

/**/
static char *
paramtypestr(Param pm)
{
    char *val = NULL;
    int f = pm->flags;

    if (!(f & PM_UNSET)) {
	if (pm->flags & PM_AUTOLOAD)
	    return dupstring("undefined");

	switch (PM_TYPE(f)) {
	case PM_SCALAR:  val = "scalar"; break;
	case PM_ARRAY:   val = "array"; break;
	case PM_INTEGER: val = "integer"; break;
	case PM_EFLOAT:
	case PM_FFLOAT:  val = "float"; break;
	case PM_HASHED:  val = "association"; break;
	}
	DPUTS(!val, "BUG: type not handled in parameter");
	val = dupstring(val);
	if (pm->level)
	    val = dyncat(val, "-local");
	if (f & PM_LEFT)
	    val = dyncat(val, "-left");
	if (f & PM_RIGHT_B)
	    val = dyncat(val, "-right_blanks");
	if (f & PM_RIGHT_Z)
	    val = dyncat(val, "-right_zeros");
	if (f & PM_LOWER)
	    val = dyncat(val, "-lower");
	if (f & PM_UPPER)
	    val = dyncat(val, "-upper");
	if (f & PM_READONLY)
	    val = dyncat(val, "-readonly");
	if (f & PM_TAGGED)
	    val = dyncat(val, "-tag");
	if (f & PM_EXPORTED)
	    val = dyncat(val, "-export");
	if (f & PM_UNIQUE)
	    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
	val = dupstring("");

    return val;
}

/**/
static HashNode
getpmparameter(UNUSED(HashTable ht), char *name)
{
    Param rpm, pm = NULL;

    pm = (Param) hcalloc(sizeof(struct param));
    pm->nam = dupstring(name);
    pm->flags = PM_SCALAR | PM_READONLY;
    pm->gsu.s = &nullsetscalar_gsu;
    if ((rpm = (Param) realparamtab->getnode(realparamtab, name)) &&
	!(rpm->flags & PM_UNSET))
	pm->u.str = paramtypestr(rpm);
    else {
	pm->u.str = dupstring("");
	pm->flags |= PM_UNSET;
    }
    return (HashNode) pm;
}

/**/
static void
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.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)) ||
		 !(flags & SCANPM_WANTKEYS)))
		pm.u.str = paramtypestr((Param) hn);
	    func((HashNode) &pm, flags);
	}
}

/* Functions for the commands special parameter. */

/**/
static void
setpmcommand(Param pm, char *value)
{
    if (isset(RESTRICTED)) {
	zwarn("restricted: %s", value, 0);
	zsfree(value);
    } else {
	Cmdnam cn = zshcalloc(sizeof(*cn));

	cn->flags = HASHED;
	cn->u.cmd = value;

	cmdnamtab->addnode(cmdnamtab, ztrdup(pm->nam), (HashNode) cn);
    }
}

/**/
static void
unsetpmcommand(Param pm, UNUSED(int exp))
{
    HashNode hn = cmdnamtab->removenode(cmdnamtab, pm->nam);

    if (hn)
	cmdnamtab->freenode(hn);
}

/**/
static void
setpmcommands(UNUSED(Param pm), HashTable ht)
{
    int i;
    HashNode hn;

    if (!ht)
	return;

    for (i = 0; i < ht->hsize; i++)
	for (hn = ht->nodes[i]; hn; hn = hn->next) {
	    Cmdnam cn = zshcalloc(sizeof(*cn));
	    struct value v;

	    v.isarr = v.inv = v.start = 0;
	    v.end = -1;
	    v.arr = NULL;
	    v.pm = (Param) hn;

	    cn->flags = HASHED;
	    cn->u.cmd = ztrdup(getstrvalue(&v));

	    cmdnamtab->addnode(cmdnamtab, ztrdup(hn->nam), (HashNode) cn);
	}
    deleteparamtable(ht);
}

static const struct gsu_scalar pmcommand_gsu =
{ strgetfn, setpmcommand, unsetpmcommand };


/**/
static HashNode
getpmcommand(UNUSED(HashTable ht), char *name)
{
    Cmdnam cmd;
    Param pm = NULL;

    if (!(cmd = (Cmdnam) cmdnamtab->getnode(cmdnamtab, name)) &&
	isset(HASHLISTALL)) {
	cmdnamtab->filltable(cmdnamtab);
	cmd = (Cmdnam) cmdnamtab->getnode(cmdnamtab, name);
    }
    pm = (Param) hcalloc(sizeof(struct param));
    pm->nam = dupstring(name);
    pm->flags = PM_SCALAR;
    pm->gsu.s = &pmcommand_gsu;
    if (cmd) {
	if (cmd->flags & HASHED)
	    pm->u.str = cmd->u.cmd;
	else {
	    pm->u.str = zhalloc(strlen(*(cmd->u.name)) + strlen(name) + 2);
	    strcpy(pm->u.str, *(cmd->u.name));
	    strcat(pm->u.str, "/");
	    strcat(pm->u.str, name);
	}
    } else {
	pm->u.str = dupstring("");
	pm->flags |= PM_UNSET;
    }
    return (HashNode) pm;
}

/**/
static void
scanpmcommands(UNUSED(HashTable ht), ScanFunc func, int flags)
{
    struct param pm;
    int i;
    HashNode hn;
    Cmdnam cmd;

    if (isset(HASHLISTALL))
	cmdnamtab->filltable(cmdnamtab);

    memset((void *)&pm, 0, sizeof(struct param));
    pm.flags = PM_SCALAR;
    pm.gsu.s = &pmcommand_gsu;

    for (i = 0; i < cmdnamtab->hsize; i++)
	for (hn = cmdnamtab->nodes[i]; hn; hn = hn->next) {
	    pm.nam = hn->nam;
	    cmd = (Cmdnam) hn;
	    if (func != scancountparams &&
		((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
		 !(flags & SCANPM_WANTKEYS))) {
		if (cmd->flags & HASHED)
		    pm.u.str = cmd->u.cmd;
		else {
		    pm.u.str = zhalloc(strlen(*(cmd->u.name)) +
				       strlen(cmd->nam) + 2);
		    strcpy(pm.u.str, *(cmd->u.name));
		    strcat(pm.u.str, "/");
		    strcat(pm.u.str, cmd->nam);
		}
	    }
	    func((HashNode) &pm, flags);
	}
}

/* Functions for the functions special parameter. */

/**/
static void
setfunction(char *name, char *val, int dis)
{
    char *value = dupstring(val);
    Shfunc shf;
    Eprog prog;
    int sn;

    val = metafy(val, strlen(val), META_REALLOC);

    prog = parse_string(val);

    if (!prog || prog == &dummy_eprog) {
	zwarn("invalid function definition", value, 0);
	zsfree(val);
	return;
    }
    shf = (Shfunc) zalloc(sizeof(*shf));
    shf->funcdef = dupeprog(prog, 0);
    shf->flags = dis;

    if (!strncmp(name, "TRAP", 4) &&
	(sn = getsignum(name + 4)) != -1) {
	if (settrap(sn, shf->funcdef)) {
	    freeeprog(shf->funcdef);
	    zfree(shf, sizeof(*shf));
	    zsfree(val);
	    return;
	}
	sigtrapped[sn] |= ZSIG_FUNC;
    }
    shfunctab->addnode(shfunctab, ztrdup(name), shf);
    zsfree(val);
}

/**/
static void
setpmfunction(Param pm, char *value)
{
    setfunction(pm->nam, value, 0);
}

/**/
static void
setpmdisfunction(Param pm, char *value)
{
    setfunction(pm->nam, value, DISABLED);
}

/**/
static void
unsetpmfunction(Param pm, UNUSED(int exp))
{
    HashNode hn = shfunctab->removenode(shfunctab, pm->nam);

    if (hn)
	shfunctab->freenode(hn);
}

/**/
static void
setfunctions(UNUSED(Param pm), HashTable ht, int dis)
{
    int i;
    HashNode hn;

    if (!ht)
	return;

    for (i = 0; i < ht->hsize; i++)
	for (hn = ht->nodes[i]; hn; hn = hn->next) {
	    struct value v;

	    v.isarr = v.inv = v.start = 0;
	    v.end = -1;
	    v.arr = NULL;
	    v.pm = (Param) hn;

	    setfunction(hn->nam, ztrdup(getstrvalue(&v)), dis);
	}
    deleteparamtable(ht);
}

/**/
static void
setpmfunctions(Param pm, HashTable ht)
{
    setfunctions(pm, ht, 0);
}

/**/
static void
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(UNUSED(HashTable ht), char *name, int dis)
{
    Shfunc shf;
    Param pm = NULL;

    pm = (Param) hcalloc(sizeof(struct param));
    pm->nam = dupstring(name);
    pm->flags = PM_SCALAR;
    pm->gsu.s = dis ? &pmdisfunction_gsu :  &pmfunction_gsu;

    if ((shf = (Shfunc) shfunctab->getnode2(shfunctab, name)) &&
	(dis ? (shf->flags & DISABLED) : !(shf->flags & DISABLED))) {
	if (shf->flags & PM_UNDEFINED) {
	    pm->u.str = dyncat("builtin autoload -X",
			       ((shf->flags & PM_UNALIASED) ?
				((shf->flags & PM_TAGGED) ? "Ut" : "U") :
				((shf->flags & PM_TAGGED) ? "t" : "")));
	} else {
	    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);

	    pm->u.str = h;
	}
    } else {
	pm->u.str = dupstring("");
	pm->flags |= PM_UNSET;
    }
    return (HashNode) pm;
}

/**/
static HashNode
getpmfunction(HashTable ht, char *name)
{
    return getfunction(ht, name, 0);
}

/**/
static HashNode
getpmdisfunction(HashTable ht, char *name)
{
    return getfunction(ht, name, DISABLED);
}

/**/
static void
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.gsu.s = dis ? &pmdisfunction_gsu : &pmfunction_gsu;

    for (i = 0; i < shfunctab->hsize; i++)
	for (hn = shfunctab->nodes[i]; hn; hn = hn->next) {
	    if (dis ? (hn->flags & DISABLED) : !(hn->flags & DISABLED)) {
		pm.nam = hn->nam;
		if (func != scancountparams &&
		    ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
		     !(flags & SCANPM_WANTKEYS))) {
		    if (((Shfunc) hn)->flags & PM_UNDEFINED) {
			Shfunc shf = (Shfunc) hn;
			pm.u.str =
			    dyncat("builtin autoload -X",
				   ((shf->flags & PM_UNALIASED) ?
				    ((shf->flags & PM_TAGGED) ? "Ut" : "U") :
				    ((shf->flags & PM_TAGGED) ? "t" : "")));
		    } else {
			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);
		    }
		}
		func((HashNode) &pm, flags);
	    }
	}
}

/**/
static void
scanpmfunctions(HashTable ht, ScanFunc func, int flags)
{
    scanfunctions(ht, func, flags, 0);
}

/**/
static void
scanpmdisfunctions(HashTable ht, ScanFunc func, int flags)
{
    scanfunctions(ht, func, flags, DISABLED);
}

/* Functions for the funcstack special parameter. */

/**/
static char **
funcstackgetfn(UNUSED(Param pm))
{
    Funcstack f;
    int num;
    char **ret, **p;

    for (f = funcstack, num = 0; f; f = f->prev, num++);

    ret = (char **) zhalloc((num + 1) * sizeof(char *));

    for (f = funcstack, p = ret; f; f = f->prev, p++)
	*p = f->name;
    *p = NULL;

    return ret;
}

/* Functions for the builtins special parameter. */

/**/
static HashNode
getbuiltin(UNUSED(HashTable ht), char *name, int dis)
{
    Param pm = NULL;
    Builtin bn;

    pm = (Param) hcalloc(sizeof(struct param));
    pm->nam = dupstring(name);
    pm->flags = PM_SCALAR | PM_READONLY;
    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)) ?
		   "defined" : "undefined");

	pm->u.str = dupstring(t);
    } else {
	pm->u.str = dupstring("");
	pm->flags |= PM_UNSET;
    }
    return (HashNode) pm;
}

/**/
static HashNode
getpmbuiltin(HashTable ht, char *name)
{
    return getbuiltin(ht, name, 0);
}

/**/
static HashNode
getpmdisbuiltin(HashTable ht, char *name)
{
    return getbuiltin(ht, name, DISABLED);
}

/**/
static void
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.gsu.s = &nullsetscalar_gsu;

    for (i = 0; i < builtintab->hsize; i++)
	for (hn = builtintab->nodes[i]; hn; hn = hn->next) {
	    if (dis ? (hn->flags & DISABLED) : !(hn->flags & DISABLED)) {
		pm.nam = hn->nam;
		if (func != scancountparams &&
		    ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
		     !(flags & SCANPM_WANTKEYS))) {
		    char *t = ((((Builtin) hn)->handlerfunc ||
				(hn->flags & BINF_PREFIX)) ?
			       "defined" : "undefined");

		    pm.u.str = dupstring(t);
		}
		func((HashNode) &pm, flags);
	    }
	}
}

/**/
static void
scanpmbuiltins(HashTable ht, ScanFunc func, int flags)
{
    scanbuiltins(ht, func, flags, 0);
}

/**/
static void
scanpmdisbuiltins(HashTable ht, ScanFunc func, int flags)
{
    scanbuiltins(ht, func, flags, DISABLED);
}

/* Functions for the reswords special parameter. */

/**/
static char **
getreswords(int dis)
{
    int i;
    HashNode hn;
    char **ret, **p;

    p = ret = (char **) zhalloc((reswdtab->ct + 1) * sizeof(char *));

    for (i = 0; i < reswdtab->hsize; i++)
	for (hn = reswdtab->nodes[i]; hn; hn = hn->next)
	    if (dis ? (hn->flags & DISABLED) : !(hn->flags & DISABLED))
		*p++ = dupstring(hn->nam);
    *p = NULL;

    return ret;
}

/**/
static char **
reswordsgetfn(UNUSED(Param pm))
{
    return getreswords(0);
}

/**/
static char **
disreswordsgetfn(UNUSED(Param pm))
{
    return getreswords(DISABLED);
}

/* Functions for the options special parameter. */

/**/
static void
setpmoption(Param pm, char *value)
{
    int n;

    if (!value || (strcmp(value, "on") && strcmp(value, "off")))
	zwarn("invalid value: %s", value, 0);
    else if (!(n = optlookup(pm->nam)))
	zwarn("no such option: %s", pm->nam, 0);
    else if (dosetopt(n, (value && strcmp(value, "off")), 0))
	zwarn("can't change option: %s", pm->nam, 0);
    zsfree(value);
}

/**/
static void
unsetpmoption(Param pm, UNUSED(int exp))
{
    int n;

    if (!(n = optlookup(pm->nam)))
	zwarn("no such option: %s", pm->nam, 0);
    else if (dosetopt(n, 0, 0))
	zwarn("can't change option: %s", pm->nam, 0);
}

/**/
static void
setpmoptions(UNUSED(Param pm), HashTable ht)
{
    int i;
    HashNode hn;

    if (!ht)
	return;

    for (i = 0; i < ht->hsize; i++)
	for (hn = ht->nodes[i]; hn; hn = hn->next) {
	    struct value v;
	    char *val;

	    v.isarr = v.inv = v.start = 0;
	    v.end = -1;
	    v.arr = NULL;
	    v.pm = (Param) hn;

	    val = getstrvalue(&v);
	    if (!val || (strcmp(val, "on") && strcmp(val, "off")))
		zwarn("invalid value: %s", val, 0);
	    else if (dosetopt(optlookup(hn->nam),
			      (val && strcmp(val, "off")), 0))
		zwarn("can't change option: %s", hn->nam, 0);
	}
    deleteparamtable(ht);
}

static const struct gsu_scalar pmoption_gsu =
{ strgetfn, setpmoption, unsetpmoption };

/**/
static HashNode
getpmoption(UNUSED(HashTable ht), char *name)
{
    Param pm = NULL;
    int n;

    pm = (Param) hcalloc(sizeof(struct param));
    pm->nam = dupstring(name);
    pm->flags = PM_SCALAR;
    pm->gsu.s = &pmoption_gsu;

    if ((n = optlookup(name)))
    {
	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;
    }
    return (HashNode) pm;
}

/**/
static void
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.gsu.s = &pmoption_gsu;

    for (i = 0; i < optiontab->hsize; i++)
	for (hn = optiontab->nodes[i]; hn; hn = hn->next) {
	    int optno = ((Optname) hn)->optno, ison;
	    pm.nam = hn->nam;
	    ison = optno < 0 ? !opts[-optno] : opts[optno];
	    pm.u.str = dupstring(ison ? "on" : "off");
	    func((HashNode) &pm, flags);
	}
}

/* Functions for the modules special parameter. */

static char *modpmname;
static int modpmfound;

/**/
static void
modpmbuiltinscan(HashNode hn, UNUSED(int dummy))
{
    if (!(((Builtin) hn)->flags & BINF_ADDED) &&
	!strcmp(((Builtin) hn)->optstr, modpmname))
	modpmfound = 1;
}

/**/
static void
modpmparamscan(HashNode hn, UNUSED(int dummy))
{
    if ((((Param) hn)->flags & PM_AUTOLOAD) &&
	!strcmp(((Param) hn)->u.str, modpmname))
	modpmfound = 1;
}

/**/
static int
findmodnode(LinkList l, char *nam)
{
    LinkNode node;

    for (node = firstnode(l); node; incnode(node))
	if (!strcmp(nam, (char *) getdata(node)))
	    return 1;

    return 0;
}

/**/
static HashNode
getpmmodule(UNUSED(HashTable ht), char *name)
{
    Param pm = NULL;
    char *type = NULL;
    LinkNode node;

    pm = (Param) hcalloc(sizeof(struct param));
    pm->nam = dupstring(name);
    pm->flags = PM_SCALAR | PM_READONLY;
    pm->gsu.s = &nullsetscalar_gsu;

    if (!type) {
	Module m;

	for (node = firstnode(modules); node; incnode(node)) {
	    m = (Module) getdata(node);
	    if (m->u.handle && !(m->flags & MOD_UNLOAD) &&
		!strcmp(name, m->nam)) {
		type = ((m->flags & MOD_ALIAS) ?
			dyncat("alias:", m->u.alias) : "loaded");
		break;
	    }
	}
    }
    modpmname = name;
    modpmfound = 0;
    if (!type) {
	scanhashtable(builtintab, 0, 0, 0, modpmbuiltinscan, 0);
	if (!modpmfound) {
	    Conddef p;

	    for (p = condtab; p; p = p->next)
		if (p->module && !strcmp(name, p->module)) {
		    modpmfound = 1;
		    break;
		}
	    if (!modpmfound)
		scanhashtable(realparamtab, 0, 0, 0, modpmparamscan, 0);
	}
	if (modpmfound)
	    type = "autoloaded";
    }
    if (type)
	pm->u.str = dupstring(type);
    else {
	pm->u.str = dupstring("");
	pm->flags |= PM_UNSET;
    }
    return (HashNode) pm;
}

/**/
static void
scanpmmodules(UNUSED(HashTable ht), ScanFunc func, int flags)
{
    struct param pm;
    int i;
    HashNode hn;
    LinkList done = newlinklist();
    LinkNode node;
    Module m;
    Conddef p;
    char *loaded = dupstring("loaded");

    memset((void *)&pm, 0, sizeof(struct param));
    pm.flags = PM_SCALAR | PM_READONLY;
    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);
	}
    }
    pm.u.str = dupstring("autoloaded");
    for (i = 0; i < builtintab->hsize; i++)
	for (hn = builtintab->nodes[i]; hn; hn = hn->next) {
	    if (!(((Builtin) hn)->flags & BINF_ADDED) &&
		!findmodnode(done, ((Builtin) hn)->optstr)) {
		pm.nam = ((Builtin) hn)->optstr;
		addlinknode(done, pm.nam);
		func((HashNode) &pm, flags);
	    }
	}
    for (p = condtab; p; p = p->next)
	if (p->module && !findmodnode(done, p->module)) {
	    pm.nam = p->module;
	    addlinknode(done, pm.nam);
	    func((HashNode) &pm, flags);
	}
    for (i = 0; i < realparamtab->hsize; i++)
	for (hn = realparamtab->nodes[i]; hn; hn = hn->next) {
	    if ((((Param) hn)->flags & PM_AUTOLOAD) &&
		!findmodnode(done, ((Param) hn)->u.str)) {
		pm.nam = ((Param) hn)->u.str;
		addlinknode(done, pm.nam);
		func((HashNode) &pm, flags);
	    }
	}
}

/* Functions for the dirstack special parameter. */

/**/
static void
dirssetfn(UNUSED(Param pm), char **x)
{
    char **ox = x;

    if (!incleanup) {
	freelinklist(dirstack, freestr);
	dirstack = znewlinklist();
	while (x && *x)
	    zaddlinknode(dirstack, ztrdup(*x++));
    }
    if (ox)
	freearray(ox);
}

/**/
static char **
dirsgetfn(UNUSED(Param pm))
{
    int l = countlinknodes(dirstack);
    char **ret = (char **) zhalloc((l + 1) * sizeof(char *)), **p;
    LinkNode n;

    for (n = firstnode(dirstack), p = ret; n; incnode(n), p++)
	*p = dupstring((char *) getdata(n));
    *p = NULL;

    return ret;
}

/* Functions for the history special parameter. */

/**/
static HashNode
getpmhistory(UNUSED(HashTable ht), char *name)
{
    Param pm = NULL;
    Histent he;
    char *p;
    int ok = 1;

    pm = (Param) hcalloc(sizeof(struct param));
    pm->nam = dupstring(name);
    pm->flags = PM_SCALAR | PM_READONLY;
    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("");
	pm->flags |= PM_UNSET;
    }
    return (HashNode) pm;
}

/**/
static void
scanpmhistory(UNUSED(HashTable ht), ScanFunc func, int flags)
{
    struct param pm;
    int i = addhistnum(curhist, -1, HIST_FOREIGN);
    Histent he = gethistent(i, GETHIST_UPWARD);
    char buf[40];

    memset((void *)&pm, 0, sizeof(struct param));
    pm.flags = PM_SCALAR | PM_READONLY;
    pm.gsu.s = &nullsetscalar_gsu;

    while (he) {
	if (func != scancountparams) {
	    convbase(buf, he->histnum, 10);
	    pm.nam = dupstring(buf);
	    if ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
		!(flags & SCANPM_WANTKEYS))
		pm.u.str = dupstring(he->text);
	}
	func((HashNode) &pm, flags);

	he = up_histent(he);
    }
}

/* Function for the historywords special parameter. */

/**/
static char **
histwgetfn(UNUSED(Param pm))
{
    char **ret, **p, *h, *e, sav;
    LinkList l = newlinklist(), ll;
    LinkNode n;
    int i = addhistnum(curhist, -1, HIST_FOREIGN), iw;
    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--) {
	    h = he->text + he->words[iw * 2];
	    e = he->text + he->words[iw * 2 + 1];
	    sav = *e;
	    *e = '\0';
	    addlinknode(l, dupstring(h));
	    *e = sav;
	}
	he = up_histent(he);
    }
    ret = (char **) zhalloc((countlinknodes(l) + 1) * sizeof(char *));

    for (p = ret, n = firstnode(l); n; incnode(n), p++)
	*p = (char *) getdata(n);
    *p = NULL;

    return ret;
}

/* Functions for the jobtexts special parameter. */

/**/
static char *
pmjobtext(int job)
{
    Process pn;
    int len = 1;
    char *ret;

    for (pn = jobtab[job].procs; pn; pn = pn->next)
	len += strlen(pn->text) + 3;

    ret = (char *) zhalloc(len);
    ret[0] = '\0';

    for (pn = jobtab[job].procs; pn; pn = pn->next) {
	strcat(ret, pn->text);
	if (pn->next)
	    strcat(ret, " | ");
    }
    return ret;
}

/**/
static HashNode
getpmjobtext(UNUSED(HashTable ht), char *name)
{
    Param pm = NULL;
    int job;

    pm = (Param) hcalloc(sizeof(struct param));
    pm->nam = dupstring(name);
    pm->flags = PM_SCALAR | PM_READONLY;
    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);
    else {
	pm->u.str = dupstring("");
	pm->flags |= PM_UNSET;
    }
    return (HashNode) pm;
}

/**/
static void
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.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) {
		sprintf(buf, "%d", job);
		pm.nam = dupstring(buf);
		if ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
		    !(flags & SCANPM_WANTKEYS))
		    pm.u.str = pmjobtext(job);
	    }
	    func((HashNode) &pm, flags);
	}
    }
}

/* Functions for the jobstates special parameter. */

/**/
static char *
pmjobstate(int job)
{
    Process pn;
    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 = dyncat("done", cp);
    else if (jobtab[job].stat & STAT_STOPPED)
	ret = dyncat("suspended", cp);
    else
	ret = dyncat("running", cp);

    for (pn = jobtab[job].procs; pn; pn = pn->next) {

	if (pn->status == SP_RUNNING)
	    state = "running";
	else if (WIFEXITED(pn->status)) {
	    if (WEXITSTATUS(pn->status))
		sprintf((state = buf2), "exit %d", (pn->status));
	    else
		state = "done";
	} else if (WIFSTOPPED(pn->status))
	    state = sigmsg(WSTOPSIG(pn->status));
	else if (WCOREDUMP(pn->status))
	    sprintf((state = buf2), "%s (core dumped)",
		    sigmsg(WTERMSIG(pn->status)));
	else
	    state = sigmsg(WTERMSIG(pn->status));

	sprintf(buf, ":%d=%s", (int)pn->pid, state);

	ret = dyncat(ret, buf);
    }
    return ret;
}

/**/
static HashNode
getpmjobstate(UNUSED(HashTable ht), char *name)
{
    Param pm = NULL;
    int job;

    pm = (Param) hcalloc(sizeof(struct param));
    pm->nam = dupstring(name);
    pm->flags = PM_SCALAR | PM_READONLY;
    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);
    else {
	pm->u.str = dupstring("");
	pm->flags |= PM_UNSET;
    }
    return (HashNode) pm;
}

/**/
static void
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.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) {
		sprintf(buf, "%d", job);
		pm.nam = dupstring(buf);
		if ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
		    !(flags & SCANPM_WANTKEYS))
		    pm.u.str = pmjobstate(job);
	    }
	    func((HashNode) &pm, flags);
	}
    }
}

/* Functions for the jobdirs special parameter. */

/**/
static char *
pmjobdir(int job)
{
    char *ret;

    ret = dupstring(jobtab[job].pwd ? jobtab[job].pwd : pwd);
    return ret;
}

/**/
static HashNode
getpmjobdir(UNUSED(HashTable ht), char *name)
{
    Param pm = NULL;
    int job;

    pm = (Param) hcalloc(sizeof(struct param));
    pm->nam = dupstring(name);
    pm->flags = PM_SCALAR | PM_READONLY;
    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);
    else {
	pm->u.str = dupstring("");
	pm->flags |= PM_UNSET;
    }
    return (HashNode) pm;
}

/**/
static void
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.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) {
	       sprintf(buf, "%d", job);
	       pm.nam = dupstring(buf);
               if ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
		   !(flags & SCANPM_WANTKEYS))
		   pm.u.str = pmjobdir(job);
	   }
           func((HashNode) &pm, flags);
       }
    }
}

/* Functions for the nameddirs special parameter. */

/**/
static void
setpmnameddir(Param pm, char *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, UNUSED(int exp))
{
    HashNode hd = nameddirtab->removenode(nameddirtab, pm->nam);

    if (hd)
	nameddirtab->freenode(hd);
}

/**/
static void
setpmnameddirs(UNUSED(Param pm), HashTable ht)
{
    int i;
    HashNode hn, next, hd;

    if (!ht)
	return;

    for (i = 0; i < nameddirtab->hsize; i++)
	for (hn = nameddirtab->nodes[i]; hn; hn = next) {
	    next = hn->next;
	    if (!(((Nameddir) hn)->flags & ND_USERNAME) &&
		(hd = nameddirtab->removenode(nameddirtab, hn->nam)))
		nameddirtab->freenode(hd);
	}

    for (i = 0; i < ht->hsize; i++)
	for (hn = ht->nodes[i]; hn; hn = hn->next) {
	    struct value v;
	    char *val;

	    v.isarr = v.inv = v.start = 0;
	    v.end = -1;
	    v.arr = NULL;
	    v.pm = (Param) hn;

	    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
     * when the sub-pms are deleted. */

    i = opts[INTERACTIVE];
    opts[INTERACTIVE] = 0;
    deleteparamtable(ht);
    opts[INTERACTIVE] = i;
}

static const struct gsu_scalar pmnamedir_gsu =
{ strgetfn, setpmnameddir, unsetpmnameddir };

/**/
static HashNode
getpmnameddir(UNUSED(HashTable ht), char *name)
{
    Param pm = NULL;
    Nameddir nd;

    pm = (Param) hcalloc(sizeof(struct param));
    pm->nam = dupstring(name);
    pm->flags = PM_SCALAR;
    pm->gsu.s = &pmnamedir_gsu;
    if ((nd = (Nameddir) nameddirtab->getnode(nameddirtab, name)) &&
	!(nd->flags & ND_USERNAME))
	pm->u.str = dupstring(nd->dir);
    else {
	pm->u.str = dupstring("");
	pm->flags |= PM_UNSET;
    }
    return (HashNode) pm;
}

/**/
static void
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.gsu.s = &pmnamedir_gsu;

    for (i = 0; i < nameddirtab->hsize; i++)
	for (hn = nameddirtab->nodes[i]; hn; hn = hn->next) {
	    if (!((nd = (Nameddir) hn)->flags & ND_USERNAME)) {
		pm.nam = hn->nam;
		if (func != scancountparams &&
		    ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
		     !(flags & SCANPM_WANTKEYS)))
		    pm.u.str = dupstring(nd->dir);
		func((HashNode) &pm, flags);
	    }
	}
}

/* Functions for the userdirs special parameter. */

/**/
static HashNode
getpmuserdir(UNUSED(HashTable ht), char *name)
{
    Param pm = NULL;
    Nameddir nd;

    nameddirtab->filltable(nameddirtab);

    pm = (Param) hcalloc(sizeof(struct param));
    pm->nam = dupstring(name);
    pm->flags = PM_SCALAR | PM_READONLY;
    pm->gsu.s = &nullsetscalar_gsu;
    if ((nd = (Nameddir) nameddirtab->getnode(nameddirtab, name)) &&
	(nd->flags & ND_USERNAME))
	pm->u.str = dupstring(nd->dir);
    else {
	pm->u.str = dupstring("");
	pm->flags |= PM_UNSET;
    }
    return (HashNode) pm;
}

/**/
static void
scanpmuserdirs(UNUSED(HashTable ht), ScanFunc func, int flags)
{
    struct param pm;
    int i;
    HashNode hn;
    Nameddir nd;

    nameddirtab->filltable(nameddirtab);

    memset((void *)&pm, 0, sizeof(struct param));
    pm.flags = PM_SCALAR | PM_READONLY;
    pm.gsu.s = &nullsetscalar_gsu;

    for (i = 0; i < nameddirtab->hsize; i++)
	for (hn = nameddirtab->nodes[i]; hn; hn = hn->next) {
	    if ((nd = (Nameddir) hn)->flags & ND_USERNAME) {
		pm.nam = hn->nam;
		if (func != scancountparams &&
		    ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
		     !(flags & SCANPM_WANTKEYS)))
		    pm.u.str = dupstring(nd->dir);
		func((HashNode) &pm, flags);
	    }
	}
}

/* Functions for the raliases, galiases and saliases special parameters. */

/**/
static void
setalias(HashTable ht, Param pm, char *value, int flags)
{
    ht->addnode(ht, ztrdup(pm->nam),
		createaliasnode(value, flags));
}

/**/
static void
setpmralias(Param pm, char *value)
{
    setalias(aliastab, pm, value, 0);
}

/**/
static void
setpmdisralias(Param pm, char *value)
{
    setalias(aliastab, pm, value, DISABLED);
}

/**/
static void
setpmgalias(Param pm, char *value)
{
    setalias(aliastab, pm, value, ALIAS_GLOBAL);
}

/**/
static void
setpmdisgalias(Param pm, char *value)
{
    setalias(aliastab, pm, value, ALIAS_GLOBAL|DISABLED);
}

/**/
static void
setpmsalias(Param pm, char *value)
{
    setalias(sufaliastab, pm, value, ALIAS_SUFFIX);
}

/**/
static void
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);

    if (hd)
	aliastab->freenode(hd);
}

/**/
static void
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;

    if (!ht)
	return;

    for (i = 0; i < alht->hsize; i++)
	for (hn = alht->nodes[i]; hn; hn = next) {
	    next = hn->next;
	    /*
	     * 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++)
	for (hn = ht->nodes[i]; hn; hn = hn->next) {
	    struct value v;
	    char *val;

	    v.isarr = v.inv = v.start = 0;
	    v.end = -1;
	    v.arr = NULL;
	    v.pm = (Param) hn;

	    if ((val = getstrvalue(&v)))
		alht->addnode(alht, ztrdup(hn->nam),
			      createaliasnode(ztrdup(val), flags));
	}
    deleteparamtable(ht);
}

/**/
static void
setpmraliases(Param pm, HashTable ht)
{
    setaliases(aliastab, pm, ht, 0);
}

/**/
static void
setpmdisraliases(Param pm, HashTable ht)
{
    setaliases(aliastab, pm, ht, DISABLED);
}

/**/
static void
setpmgaliases(Param pm, HashTable ht)
{
    setaliases(aliastab, pm, ht, ALIAS_GLOBAL);
}

/**/
static void
setpmdisgaliases(Param pm, HashTable ht)
{
    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 alht, UNUSED(HashTable ht), char *name, int flags)
{
    Param pm = NULL;
    Alias al;

    pm = (Param) hcalloc(sizeof(struct param));
    pm->nam = dupstring(name);

    assignaliasdefs(pm, flags);

    if ((al = (Alias) alht->getnode2(alht, name)) &&
	flags == al->flags)
	pm->u.str = dupstring(al->text);
    else {
	pm->u.str = dupstring("");
	pm->flags |= PM_UNSET;
    }
    return (HashNode) pm;
}

/**/
static HashNode
getpmralias(HashTable ht, char *name)
{
    return getalias(aliastab, ht, name, 0);
}

/**/
static HashNode
getpmdisralias(HashTable ht, char *name)
{
    return getalias(aliastab, ht, name, DISABLED);
}

/**/
static HashNode
getpmgalias(HashTable ht, char *name)
{
    return getalias(aliastab, ht, name, ALIAS_GLOBAL);
}

/**/
static HashNode
getpmdisgalias(HashTable ht, char *name)
{
    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 alht, UNUSED(HashTable ht), ScanFunc func,
	    int pmflags, int alflags)
{
    struct param pm;
    int i;
    Alias al;

    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 &&
		    ((pmflags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
		     !(pmflags & SCANPM_WANTKEYS)))
		    pm.u.str = dupstring(al->text);
		func((HashNode) &pm, pmflags);
	    }
	}
}

/**/
static void
scanpmraliases(HashTable ht, ScanFunc func, int flags)
{
    scanaliases(aliastab, ht, func, flags, 0);
}

/**/
static void
scanpmdisraliases(HashTable ht, ScanFunc func, int flags)
{
    scanaliases(aliastab, ht, func, flags, DISABLED);
}

/**/
static void
scanpmgaliases(HashTable ht, ScanFunc func, int flags)
{
    scanaliases(aliastab, ht, func, flags, ALIAS_GLOBAL);
}

/**/
static void
scanpmdisgaliases(HashTable ht, ScanFunc func, int flags)
{
    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. */

struct pardef {
    char *name;
    int flags;
    GetNodeFunc getnfn;
    ScanTabFunc scantfn;
    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, &pmnullsethash_gsu,
      NULL, NULL },
    { "commands", 0,
      getpmcommand, scanpmcommands, &pmcommands_gsu,
      NULL, NULL },
    { "functions", 0,
      getpmfunction, scanpmfunctions, &pmfunctions_gsu,
      NULL, NULL },
    { "dis_functions", 0,
      getpmdisfunction, scanpmdisfunctions, &pmdisfunctions_gsu,
      NULL, NULL },
    { "funcstack", PM_ARRAY|PM_SPECIAL|PM_READONLY,
      NULL, NULL, NULL,
      &funcstack_gsu, NULL },
    { "builtins", PM_READONLY,
      getpmbuiltin, scanpmbuiltins, NULL,
      NULL, NULL },
    { "dis_builtins", PM_READONLY,
      getpmdisbuiltin, scanpmdisbuiltins,
      NULL, NULL, },
    { "reswords", PM_ARRAY|PM_SPECIAL|PM_READONLY,
      NULL, NULL, NULL,
      &reswords_gsu, NULL },
    { "dis_reswords", PM_ARRAY|PM_SPECIAL|PM_READONLY,
      NULL, NULL, NULL,
      &disreswords_gsu, NULL },
    { "options", 0,
      getpmoption, scanpmoptions, &pmoptions_gsu,
      NULL, NULL },
    { "modules", PM_READONLY,
      getpmmodule, scanpmmodules, NULL,
      NULL, NULL },
    { "dirstack", PM_ARRAY|PM_SPECIAL|PM_REMOVABLE,
      NULL, NULL, NULL,
      &dirs_gsu, NULL },
    { "history", PM_READONLY,
      getpmhistory, scanpmhistory, NULL,
      NULL, NULL,  },
    { "historywords", PM_ARRAY|PM_SPECIAL|PM_READONLY,
      NULL, NULL, NULL,
      &historywords_gsu, NULL },
    { "jobtexts", PM_READONLY,
      getpmjobtext, scanpmjobtexts, NULL,
      NULL, NULL },
    { "jobstates", PM_READONLY,
      getpmjobstate, scanpmjobstates, NULL,
      NULL, NULL },
    { "jobdirs", PM_READONLY,
      getpmjobdir, scanpmjobdirs, NULL,
      NULL, NULL },
    { "nameddirs", 0,
      getpmnameddir, scanpmnameddirs, &pmnameddirs_gsu,
      NULL, NULL },
    { "userdirs", PM_READONLY,
      getpmuserdir, scanpmuserdirs, NULL,
      NULL, NULL },
    { "aliases", 0,
      getpmralias, scanpmraliases, &pmraliases_gsu,
      NULL, NULL },
    { "galiases", 0,
      getpmgalias, scanpmgaliases, &pmgaliases_gsu,
      NULL, NULL },
    { "saliases", 0,
      getpmsalias, scanpmsaliases, &pmsaliases_gsu,
      NULL, NULL },
    { "dis_aliases", 0,
      getpmdisralias, scanpmdisraliases, &pmdisraliases_gsu,
      NULL, NULL },
    { "dis_galiases", 0,
      getpmdisgalias, scanpmdisgaliases, &pmdisgaliases_gsu,
      NULL, NULL },
    { "dis_saliases", 0,
      getpmdissalias, scanpmdissaliases, &pmdissaliases_gsu,
      NULL, NULL },
    { NULL, 0, NULL, NULL, NULL, NULL, NULL }
};

/**/
int
setup_(UNUSED(Module m))
{
    incleanup = 0;

    return 0;
}

/**/
int
boot_(UNUSED(Module m))
{
    /* Create the special associative arrays.
     * As an example for autoloaded parameters, this is probably a bad
     * example, because the zsh core doesn't support creation of
     * special hashes, yet. */

    struct pardef *def;

    for (def = partab; def->name; def++) {
	unsetparam(def->name);

	if (def->getnfn) {
	    if (!(def->pm = createspecialhash(def->name, def->getnfn,
					      def->scantfn)))
		return 1;
	    def->pm->flags |= def->flags;
	    if (def->hash_gsu)
		def->pm->gsu.h = def->hash_gsu;
	} else {
	    if (!(def->pm = createparam(def->name, def->flags | PM_HIDE|
					PM_HIDEVAL | PM_REMOVABLE)))
		return 1;
	    def->pm->gsu.a = def->array_gsu;
	}
    }
    return 0;
}

/**/
int
cleanup_(UNUSED(Module m))
{
    Param pm;
    struct pardef *def;

    incleanup = 1;

    for (def = partab; def->name; def++) {
	if ((pm = (Param) paramtab->getnode(paramtab, def->name)) &&
	    pm == def->pm) {
	    pm->flags &= ~PM_READONLY;
	    unsetparam_pm(pm, 0, 1);
	}
    }
    return 0;
}

/**/
int
finish_(UNUSED(Module m))
{
    return 0;
}
