mirror of
				git://git.code.sf.net/p/zsh/code
				synced 2025-10-31 06:00:54 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			2368 lines
		
	
	
	
		
			55 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2368 lines
		
	
	
	
		
			55 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * 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;
 | |
| 
 | |
| /* 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->node.flags;
 | |
| 
 | |
|     if (!(f & PM_UNSET)) {
 | |
| 	if (pm->node.flags & PM_AUTOLOAD)
 | |
| 	    return dupstring("undefined");
 | |
| 
 | |
| 	/* For simplicity we treat PM_NAMEREF as PM_TYPE(PM_SCALAR) */
 | |
| 	switch (PM_TYPE(f)|(f & PM_NAMEREF)) {
 | |
| 	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;
 | |
| 	case PM_NAMEREF: val = "nameref"; 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_TIED)
 | |
| 	    val = dyncat(val, "-tied");
 | |
| 	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), const char *name)
 | |
| {
 | |
|     Param rpm, pm = NULL;
 | |
| 
 | |
|     pm = (Param) hcalloc(sizeof(struct param));
 | |
|     pm->node.nam = dupstring(name);
 | |
|     pm->node.flags = PM_SCALAR | PM_READONLY;
 | |
|     pm->gsu.s = &nullsetscalar_gsu;
 | |
|     if ((rpm = (Param) realparamtab->getnode2(realparamtab, name)) &&
 | |
| 	!(rpm->node.flags & PM_UNSET)) {
 | |
| 	pm->u.str = paramtypestr(rpm);
 | |
| 	if ((rpm->node.flags & PM_NAMEREF) && rpm->u.str && *(rpm->u.str) &&
 | |
| 	    (rpm = (Param) realparamtab->getnode(realparamtab, name)) &&
 | |
| 	    !(rpm->node.flags & PM_UNSET)) {
 | |
| 	    pm->u.str = zhtricat(pm->u.str, "-", paramtypestr(rpm));
 | |
| 	}
 | |
|     } else {
 | |
| 	pm->u.str = dupstring("");
 | |
| 	pm->node.flags |= (PM_UNSET|PM_SPECIAL);
 | |
|     }
 | |
|     return &pm->node;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| 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.node.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)->node.flags & PM_UNSET)
 | |
| 		continue;
 | |
| 	    pm.node.nam = hn->nam;
 | |
| 	    if (func != scancountparams &&
 | |
| 		((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
 | |
| 		 !(flags & SCANPM_WANTKEYS)))
 | |
| 		pm.u.str = paramtypestr((Param) hn);
 | |
| 	    func(&pm.node, flags);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /* Functions for the commands special parameter. */
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| setpmcommand(Param pm, char *value)
 | |
| {
 | |
|     if (isset(RESTRICTED)) {
 | |
| 	zwarn("restricted: %s", value);
 | |
| 	zsfree(value);
 | |
|     } else {
 | |
| 	Cmdnam cn = zshcalloc(sizeof(*cn));
 | |
| 
 | |
| 	cn->node.flags = HASHED;
 | |
| 	cn->u.cmd = value;
 | |
| 
 | |
| 	cmdnamtab->addnode(cmdnamtab, ztrdup(pm->node.nam), &cn->node);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| unsetpmcommand(Param pm, UNUSED(int exp))
 | |
| {
 | |
|     HashNode hn = cmdnamtab->removenode(cmdnamtab, pm->node.nam);
 | |
| 
 | |
|     if (hn)
 | |
| 	cmdnamtab->freenode(hn);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| setpmcommands(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.flags = v.start = 0;
 | |
| 	    v.end = -1;
 | |
| 	    v.arr = NULL;
 | |
| 	    v.pm = (Param) hn;
 | |
| 
 | |
| 	    cn->node.flags = HASHED;
 | |
| 	    cn->u.cmd = ztrdup(getstrvalue(&v));
 | |
| 
 | |
| 	    cmdnamtab->addnode(cmdnamtab, ztrdup(hn->nam), &cn->node);
 | |
| 	}
 | |
|     /*
 | |
|      * On full-array assignment ht is a temporary hash with the default
 | |
|      * get/set functions, whereas pm->u.hash has the special $commands
 | |
|      * get/set functions.  Do not assign ht to pm, just delete it.
 | |
|      *
 | |
|      * On append, ht and pm->u.hash are the same table, don't delete.
 | |
|      */
 | |
|     if (ht != pm->u.hash)
 | |
| 	deleteparamtable(ht);
 | |
| }
 | |
| 
 | |
| static const struct gsu_scalar pmcommand_gsu =
 | |
| { strgetfn, setpmcommand, unsetpmcommand };
 | |
| 
 | |
| 
 | |
| /**/
 | |
| static HashNode
 | |
| getpmcommand(UNUSED(HashTable ht), const 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->node.nam = dupstring(name);
 | |
|     pm->node.flags = PM_SCALAR;
 | |
|     pm->gsu.s = &pmcommand_gsu;
 | |
|     if (cmd) {
 | |
| 	if (cmd->node.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->node.flags |= (PM_UNSET|PM_SPECIAL);
 | |
|     }
 | |
|     return &pm->node;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| 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.node.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.node.nam = hn->nam;
 | |
| 	    cmd = (Cmdnam) hn;
 | |
| 	    if (func != scancountparams &&
 | |
| 		((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
 | |
| 		 !(flags & SCANPM_WANTKEYS))) {
 | |
| 		if (cmd->node.flags & HASHED)
 | |
| 		    pm.u.str = cmd->u.cmd;
 | |
| 		else {
 | |
| 		    pm.u.str = zhalloc(strlen(*(cmd->u.name)) +
 | |
| 				       strlen(cmd->node.nam) + 2);
 | |
| 		    strcpy(pm.u.str, *(cmd->u.name));
 | |
| 		    strcat(pm.u.str, "/");
 | |
| 		    strcat(pm.u.str, cmd->node.nam);
 | |
| 		}
 | |
| 	    }
 | |
| 	    func(&pm.node, 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, 1);
 | |
| 
 | |
|     if (!prog || prog == &dummy_eprog) {
 | |
| 	zwarn("invalid function definition", value);
 | |
| 	zsfree(val);
 | |
| 	return;
 | |
|     }
 | |
|     shf = (Shfunc) zshcalloc(sizeof(*shf));
 | |
|     shf->funcdef = dupeprog(prog, 0);
 | |
|     shf->node.flags = dis;
 | |
|     shfunc_set_sticky(shf);
 | |
| 
 | |
|     if (!strncmp(name, "TRAP", 4) &&
 | |
| 	(sn = getsignum(name + 4)) != -1) {
 | |
| 	if (settrap(sn, NULL, ZSIG_FUNC)) {
 | |
| 	    freeeprog(shf->funcdef);
 | |
| 	    zfree(shf, sizeof(*shf));
 | |
| 	    zsfree(val);
 | |
| 	    return;
 | |
| 	}
 | |
|     }
 | |
|     shfunctab->addnode(shfunctab, ztrdup(name), shf);
 | |
|     zsfree(val);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| setpmfunction(Param pm, char *value)
 | |
| {
 | |
|     setfunction(pm->node.nam, value, 0);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| setpmdisfunction(Param pm, char *value)
 | |
| {
 | |
|     setfunction(pm->node.nam, value, DISABLED);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| unsetpmfunction(Param pm, UNUSED(int exp))
 | |
| {
 | |
|     HashNode hn = shfunctab->removenode(shfunctab, pm->node.nam);
 | |
| 
 | |
|     if (hn)
 | |
| 	shfunctab->freenode(hn);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| setfunctions(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.flags = v.start = 0;
 | |
| 	    v.end = -1;
 | |
| 	    v.arr = NULL;
 | |
| 	    v.pm = (Param) hn;
 | |
| 
 | |
| 	    setfunction(hn->nam, ztrdup(getstrvalue(&v)), dis);
 | |
| 	}
 | |
|     /* See setpmcommands() above */
 | |
|     if (ht != pm->u.hash)
 | |
| 	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), const char *name, int dis)
 | |
| {
 | |
|     Shfunc shf;
 | |
|     Param pm = NULL;
 | |
| 
 | |
|     pm = (Param) hcalloc(sizeof(struct param));
 | |
|     pm->node.nam = dupstring(name);
 | |
|     pm->node.flags = PM_SCALAR;
 | |
|     pm->gsu.s = dis ? &pmdisfunction_gsu :  &pmfunction_gsu;
 | |
| 
 | |
|     if ((shf = (Shfunc) shfunctab->getnode2(shfunctab, name)) &&
 | |
| 	(dis ? (shf->node.flags & DISABLED) : !(shf->node.flags & DISABLED))) {
 | |
| 	if (shf->node.flags & PM_UNDEFINED) {
 | |
| 	    pm->u.str = dyncat("builtin autoload -X",
 | |
| 			       ((shf->node.flags & PM_UNALIASED) ?
 | |
| 				((shf->node.flags & PM_TAGGED) ? "Ut" : "U") :
 | |
| 				((shf->node.flags & PM_TAGGED) ? "t" : "")));
 | |
| 	} else {
 | |
| 	    char *t = getpermtext(shf->funcdef, NULL, 1), *n, *h;
 | |
| 	    char *start;
 | |
| 
 | |
| 	    if (shf->redir)
 | |
| 		start = "{\n\t";
 | |
| 	    else
 | |
| 		start = "\t";
 | |
| 
 | |
| 	    if (shf->funcdef->flags & EF_RUN) {
 | |
| 		n = nicedupstring(name);
 | |
| 		h = (char *) zhalloc(strlen(start) + strlen(t) + strlen(n) + 8);
 | |
| 		strcpy(h, start);
 | |
| 		strcat(h, t);
 | |
| 		strcat(h, "\n\t");
 | |
| 		strcat(h, n);
 | |
| 		strcat(h, " \"$@\"");
 | |
| 	    } else
 | |
| 		h = dyncat(start, t);
 | |
| 	    zsfree(t);
 | |
| 
 | |
| 	    if (shf->redir) {
 | |
| 		t = getpermtext(shf->redir, NULL, 1);
 | |
| 		h = zhtricat(h, "\n}", t);
 | |
| 		zsfree(t);
 | |
| 	    }
 | |
| 
 | |
| 	    pm->u.str = h;
 | |
| 	}
 | |
|     } else {
 | |
| 	pm->u.str = dupstring("");
 | |
| 	pm->node.flags |= (PM_UNSET|PM_SPECIAL);
 | |
|     }
 | |
|     return &pm->node;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static HashNode
 | |
| getpmfunction(HashTable ht, const char *name)
 | |
| {
 | |
|     return getfunction(ht, name, 0);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static HashNode
 | |
| getpmdisfunction(HashTable ht, const 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.node.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.node.nam = hn->nam;
 | |
| 		if (func != scancountparams &&
 | |
| 		    ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
 | |
| 		     !(flags & SCANPM_WANTKEYS))) {
 | |
| 		    if (((Shfunc) hn)->node.flags & PM_UNDEFINED) {
 | |
| 			Shfunc shf = (Shfunc) hn;
 | |
| 			pm.u.str =
 | |
| 			    dyncat("builtin autoload -X",
 | |
| 				   ((shf->node.flags & PM_UNALIASED) ?
 | |
| 				    ((shf->node.flags & PM_TAGGED) ? "Ut" : "U") :
 | |
| 				    ((shf->node.flags & PM_TAGGED) ? "t" : "")));
 | |
| 		    } else {
 | |
| 			Shfunc shf = (Shfunc)hn;
 | |
| 			char *t = getpermtext(shf->funcdef, NULL, 1);
 | |
| 			char *n, *start;
 | |
| 
 | |
| 			if (shf->redir)
 | |
| 			    start = "{\n\t";
 | |
| 			else
 | |
| 			    start = "\t";
 | |
| 
 | |
| 			if (shf->funcdef->flags & EF_RUN) {
 | |
| 			    n = nicedupstring(hn->nam);
 | |
| 			    pm.u.str = (char *) zhalloc(
 | |
| 				strlen(start) + strlen(t) + strlen(n) + 8);
 | |
| 			    strcpy(pm.u.str, start);
 | |
| 			    strcat(pm.u.str, t);
 | |
| 			    strcat(pm.u.str, "\n\t");
 | |
| 			    strcat(pm.u.str, n);
 | |
| 			    strcat(pm.u.str, " \"$@\"");
 | |
| 			} else
 | |
| 			    pm.u.str = dyncat(start, t);
 | |
| 			zsfree(t);
 | |
| 
 | |
| 			if (shf->redir) {
 | |
| 			    t = getpermtext(shf->redir, NULL, 1);
 | |
| 			    pm.u.str = zhtricat(pm.u.str, "\n}", t);
 | |
| 			    zsfree(t);
 | |
| 			}
 | |
| 		    }
 | |
| 		}
 | |
| 		func(&pm.node, 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 functions_source special parameter. */
 | |
| 
 | |
| /* Retrieve the source file for a function by explicit name */
 | |
| 
 | |
| /**/
 | |
| static HashNode
 | |
| getfunction_source(UNUSED(HashTable ht), const char *name, int dis)
 | |
| {
 | |
|     Shfunc shf;
 | |
|     Param pm = NULL;
 | |
| 
 | |
|     pm = (Param) hcalloc(sizeof(struct param));
 | |
|     pm->node.nam = dupstring(name);
 | |
|     pm->node.flags = PM_SCALAR|PM_READONLY;
 | |
|     pm->gsu.s = dis ? &pmdisfunction_gsu :  &pmfunction_gsu;
 | |
| 
 | |
|     if ((shf = (Shfunc) shfunctab->getnode2(shfunctab, name)) &&
 | |
| 	(dis ? (shf->node.flags & DISABLED) : !(shf->node.flags & DISABLED))) {
 | |
| 	pm->u.str = getshfuncfile(shf);
 | |
| 	if (!pm->u.str)
 | |
| 	    pm->u.str = dupstring("");
 | |
|     }
 | |
|     return &pm->node;
 | |
| }
 | |
| 
 | |
| /* Retrieve the source file for functions by scanning the table */
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| scanfunctions_source(UNUSED(HashTable ht), ScanFunc func, int flags, int dis)
 | |
| {
 | |
|     struct param pm;
 | |
|     int i;
 | |
|     HashNode hn;
 | |
| 
 | |
|     memset((void *)&pm, 0, sizeof(struct param));
 | |
|     pm.node.flags = PM_SCALAR|PM_READONLY;
 | |
|     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.node.nam = hn->nam;
 | |
| 		if (func != scancountparams &&
 | |
| 		    ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
 | |
| 		     !(flags & SCANPM_WANTKEYS))) {
 | |
| 		    pm.u.str = getshfuncfile((Shfunc)hn);
 | |
| 		    if (!pm.u.str)
 | |
| 			pm.u.str = dupstring("");
 | |
| 		}
 | |
| 		func(&pm.node, flags);
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* Param table entry for retrieving functions_source element */
 | |
| 
 | |
| /**/
 | |
| static HashNode
 | |
| getpmfunction_source(HashTable ht, const char *name)
 | |
| {
 | |
|     return getfunction_source(ht, name, 0);
 | |
| }
 | |
| 
 | |
| /* Param table entry for retrieving dis_functions_source element */
 | |
| 
 | |
| /**/
 | |
| static HashNode
 | |
| getpmdisfunction_source(HashTable ht, const char *name)
 | |
| {
 | |
|     return getfunction_source(ht, name, 1);
 | |
| }
 | |
| 
 | |
| /* Param table entry for scanning functions_source table */
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| scanpmfunction_source(HashTable ht, ScanFunc func, int flags)
 | |
| {
 | |
|     scanfunctions_source(ht, func, flags, 0);
 | |
| }
 | |
| 
 | |
| /* Param table entry for scanning dis_functions_source table */
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| scanpmdisfunction_source(HashTable ht, ScanFunc func, int flags)
 | |
| {
 | |
|     scanfunctions_source(ht, func, flags, 1);
 | |
| }
 | |
| 
 | |
| /* 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 functrace special parameter. */
 | |
| 
 | |
| /**/
 | |
| static char **
 | |
| functracegetfn(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++) {
 | |
| 	char *colonpair;
 | |
| 
 | |
| 	colonpair = zhalloc(strlen(f->caller) + (f->lineno > 9999 ? 24 : 6));
 | |
| #if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD)
 | |
| 	sprintf(colonpair, "%s:%lld", f->caller, f->lineno);
 | |
| #else
 | |
| 	sprintf(colonpair, "%s:%ld", f->caller, (long)f->lineno);
 | |
| #endif
 | |
| 
 | |
| 	*p = colonpair;
 | |
|     }
 | |
|     *p = NULL;
 | |
| 
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| /* Functions for the funcsourcetrace special parameter. */
 | |
| 
 | |
| /**/
 | |
| static char **
 | |
| funcsourcetracegetfn(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++) {
 | |
| 	char *colonpair;
 | |
| 	char *fname = f->filename ? f->filename : "";
 | |
| 
 | |
| 	colonpair = zhalloc(strlen(fname) + (f->flineno > 9999 ? 24 : 6));
 | |
| #if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD)
 | |
| 	sprintf(colonpair, "%s:%lld", fname, f->flineno);
 | |
| #else
 | |
| 	sprintf(colonpair, "%s:%ld", fname, (long)f->flineno);
 | |
| #endif
 | |
| 
 | |
| 	*p = colonpair;
 | |
|     }
 | |
|     *p = NULL;
 | |
| 
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| /* Functions for the funcfiletrace special parameter. */
 | |
| 
 | |
| /**/
 | |
| static char **
 | |
| funcfiletracegetfn(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++) {
 | |
| 	char *colonpair, *fname;
 | |
| 
 | |
| 	if (!f->prev || f->prev->tp == FS_SOURCE) {
 | |
| 	    /*
 | |
| 	     * Calling context is a file---either the parent
 | |
| 	     * script or interactive shell, or a sourced
 | |
| 	     * script.  Just print the file information for the caller
 | |
| 	     * (same as $functrace)
 | |
| 	     */
 | |
| 	    colonpair = zhalloc(strlen(f->caller) +
 | |
| 				(f->lineno > 9999 ? 24 : 6));
 | |
| #if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD)
 | |
| 	    sprintf(colonpair, "%s:%lld", f->caller, f->lineno);
 | |
| #else
 | |
| 	    sprintf(colonpair, "%s:%ld", f->caller, (long)f->lineno);
 | |
| #endif
 | |
| 	} else {
 | |
| 	    /*
 | |
| 	     * Calling context is a function or eval; we need to find
 | |
| 	     * the line number in the file where that function was
 | |
| 	     * defined or the eval was called.  For this we need the
 | |
| 	     * $funcsourcetrace information for the context above,
 | |
| 	     * together with the $functrace line number for the current
 | |
| 	     * context.
 | |
| 	     */
 | |
| 	    zlong flineno = f->prev->flineno + f->lineno;
 | |
| 	    /*
 | |
| 	     * Line numbers in eval start from 1, not zero,
 | |
| 	     * so offset by one to get line in file.
 | |
| 	     */
 | |
| 	    if (f->prev->tp == FS_EVAL)
 | |
| 		flineno--;
 | |
| 	    fname = f->prev->filename ? f->prev->filename : "";
 | |
| 
 | |
| 	    colonpair = zhalloc(strlen(fname) + (flineno > 9999 ? 24 : 6));
 | |
| #if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD)
 | |
| 	    sprintf(colonpair, "%s:%lld", fname, flineno);
 | |
| #else
 | |
| 	    sprintf(colonpair, "%s:%ld", fname, (long)flineno);
 | |
| #endif
 | |
| 	}
 | |
| 
 | |
| 	*p = colonpair;
 | |
|     }
 | |
|     *p = NULL;
 | |
| 
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| /* Functions for the builtins special parameter. */
 | |
| 
 | |
| /**/
 | |
| static HashNode
 | |
| getbuiltin(UNUSED(HashTable ht), const char *name, int dis)
 | |
| {
 | |
|     Param pm = NULL;
 | |
|     Builtin bn;
 | |
| 
 | |
|     pm = (Param) hcalloc(sizeof(struct param));
 | |
|     pm->node.nam = dupstring(name);
 | |
|     pm->node.flags = PM_SCALAR | PM_READONLY;
 | |
|     pm->gsu.s = &nullsetscalar_gsu;
 | |
|     if ((bn = (Builtin) builtintab->getnode2(builtintab, name)) &&
 | |
| 	(dis ? (bn->node.flags & DISABLED) : !(bn->node.flags & DISABLED))) {
 | |
| 	char *t = ((bn->handlerfunc || (bn->node.flags & BINF_PREFIX)) ?
 | |
| 		   "defined" : "undefined");
 | |
| 
 | |
| 	pm->u.str = dupstring(t);
 | |
|     } else {
 | |
| 	pm->u.str = dupstring("");
 | |
| 	pm->node.flags |= (PM_UNSET|PM_SPECIAL);
 | |
|     }
 | |
|     return &pm->node;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static HashNode
 | |
| getpmbuiltin(HashTable ht, const char *name)
 | |
| {
 | |
|     return getbuiltin(ht, name, 0);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static HashNode
 | |
| getpmdisbuiltin(HashTable ht, const 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.node.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.node.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(&pm.node, 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 patchars special parameter. */
 | |
| 
 | |
| /**/
 | |
| static char **
 | |
| getpatchars(int dis)
 | |
| {
 | |
|     int i;
 | |
|     char **ret, **p;
 | |
| 
 | |
|     p = ret = (char **) zhalloc(ZPC_COUNT * sizeof(char *));
 | |
| 
 | |
|     for (i = 0; i < ZPC_COUNT; i++)
 | |
| 	if (zpc_strings[i] && !dis == !zpc_disables[i])
 | |
| 	    *p++ = dupstring(zpc_strings[i]);
 | |
| 
 | |
|     *p = NULL;
 | |
| 
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| static char **
 | |
| patcharsgetfn(UNUSED(Param pm))
 | |
| {
 | |
|     return getpatchars(0);
 | |
| }
 | |
| 
 | |
| static char **
 | |
| dispatcharsgetfn(UNUSED(Param pm))
 | |
| {
 | |
|     return getpatchars(1);
 | |
| }
 | |
| 
 | |
| /* 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);
 | |
|     else if (!(n = optlookup(pm->node.nam)))
 | |
| 	zwarn("no such option: %s", pm->node.nam);
 | |
|     else if (dosetopt(n, (value && strcmp(value, "off")), 0, opts))
 | |
| 	zwarn("can't change option: %s", pm->node.nam);
 | |
|     zsfree(value);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| unsetpmoption(Param pm, UNUSED(int exp))
 | |
| {
 | |
|     int n;
 | |
| 
 | |
|     if (!(n = optlookup(pm->node.nam)))
 | |
| 	zwarn("no such option: %s", pm->node.nam);
 | |
|     else if (dosetopt(n, 0, 0, opts))
 | |
| 	zwarn("can't change option: %s", pm->node.nam);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| setpmoptions(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.flags = 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);
 | |
| 	    else if (dosetopt(optlookup(hn->nam),
 | |
| 			      (val && strcmp(val, "off")), 0, opts))
 | |
| 		zwarn("can't change option: %s", hn->nam);
 | |
| 	}
 | |
|     /* See setpmcommands() above */
 | |
|     if (ht != pm->u.hash)
 | |
| 	deleteparamtable(ht);
 | |
| }
 | |
| 
 | |
| static const struct gsu_scalar pmoption_gsu =
 | |
| { strgetfn, setpmoption, unsetpmoption };
 | |
| 
 | |
| /**/
 | |
| static HashNode
 | |
| getpmoption(UNUSED(HashTable ht), const char *name)
 | |
| {
 | |
|     Param pm = NULL;
 | |
|     int n;
 | |
| 
 | |
|     pm = (Param) hcalloc(sizeof(struct param));
 | |
|     pm->node.nam = dupstring(name);
 | |
|     pm->node.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->node.flags |= (PM_UNSET|PM_SPECIAL);
 | |
|     }
 | |
|     return &pm->node;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| 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.node.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.node.nam = hn->nam;
 | |
| 	    ison = optno < 0 ? !opts[-optno] : opts[optno];
 | |
| 	    pm.u.str = dupstring(ison ? "on" : "off");
 | |
| 	    func(&pm.node, flags);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /* Functions for the modules special parameter. */
 | |
| 
 | |
| /**/
 | |
| static HashNode
 | |
| getpmmodule(UNUSED(HashTable ht), const char *name)
 | |
| {
 | |
|     Param pm = NULL;
 | |
|     char *type = NULL;
 | |
|     Module m;
 | |
| 
 | |
|     pm = (Param) hcalloc(sizeof(struct param));
 | |
|     pm->node.nam = dupstring(name);
 | |
|     pm->node.flags = PM_SCALAR | PM_READONLY;
 | |
|     pm->gsu.s = &nullsetscalar_gsu;
 | |
| 
 | |
|     m = (Module)modulestab->getnode2(modulestab, name);
 | |
| 
 | |
|     if (!m)
 | |
| 	return NULL;
 | |
|     if (m->u.handle && !(m->node.flags & MOD_UNLOAD)) {
 | |
| 	type = ((m->node.flags & MOD_ALIAS) ?
 | |
| 		dyncat("alias:", m->u.alias) : "loaded");
 | |
|     }
 | |
|     if (!type) {
 | |
| 	if (m->autoloads && firstnode(m->autoloads))
 | |
| 	    type = "autoloaded";
 | |
|     }
 | |
|     if (type)
 | |
| 	pm->u.str = dupstring(type);
 | |
|     else {
 | |
| 	pm->u.str = dupstring("");
 | |
| 	pm->node.flags |= (PM_UNSET|PM_SPECIAL);
 | |
|     }
 | |
|     return &pm->node;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| scanpmmodules(UNUSED(HashTable ht), ScanFunc func, int flags)
 | |
| {
 | |
|     struct param pm;
 | |
|     int i;
 | |
|     HashNode hn;
 | |
|     LinkList done = newlinklist();
 | |
|     Module m;
 | |
|     Conddef p;
 | |
|     char *loaded = dupstring("loaded");
 | |
| 
 | |
|     memset((void *)&pm, 0, sizeof(struct param));
 | |
|     pm.node.flags = PM_SCALAR | PM_READONLY;
 | |
|     pm.gsu.s = &nullsetscalar_gsu;
 | |
| 
 | |
|     for (i = 0; i < modulestab->hsize; i++) {
 | |
| 	for (hn = modulestab->nodes[i]; hn; hn = hn->next) {
 | |
| 	    m = (Module) hn;
 | |
| 	    if (m->u.handle && !(m->node.flags & MOD_UNLOAD)) {
 | |
| 		pm.node.nam = m->node.nam;
 | |
| 		pm.u.str = ((m->node.flags & MOD_ALIAS) ?
 | |
| 			    dyncat("alias:", m->u.alias) : loaded);
 | |
| 		addlinknode(done, pm.node.nam);
 | |
| 		func(&pm.node, 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)->node.flags & BINF_ADDED) &&
 | |
| 		!linknodebystring(done, ((Builtin) hn)->optstr)) {
 | |
| 		pm.node.nam = ((Builtin) hn)->optstr;
 | |
| 		addlinknode(done, pm.node.nam);
 | |
| 		func(&pm.node, flags);
 | |
| 	    }
 | |
| 	}
 | |
|     for (p = condtab; p; p = p->next)
 | |
| 	if (p->module && !linknodebystring(done, p->module)) {
 | |
| 	    pm.node.nam = p->module;
 | |
| 	    addlinknode(done, pm.node.nam);
 | |
| 	    func(&pm.node, flags);
 | |
| 	}
 | |
|     for (i = 0; i < realparamtab->hsize; i++)
 | |
| 	for (hn = realparamtab->nodes[i]; hn; hn = hn->next) {
 | |
| 	    if ((((Param) hn)->node.flags & PM_AUTOLOAD) &&
 | |
| 		!linknodebystring(done, ((Param) hn)->u.str)) {
 | |
| 		pm.node.nam = ((Param) hn)->u.str;
 | |
| 		addlinknode(done, pm.node.nam);
 | |
| 		func(&pm.node, 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))
 | |
| {
 | |
|     return hlinklist2array(dirstack, 1);
 | |
| }
 | |
| 
 | |
| /* Functions for the history special parameter. */
 | |
| 
 | |
| /**/
 | |
| static HashNode
 | |
| getpmhistory(UNUSED(HashTable ht), const char *name)
 | |
| {
 | |
|     Param pm = NULL;
 | |
|     Histent he;
 | |
|     const char *p;
 | |
|     int ok = 1;
 | |
| 
 | |
|     pm = (Param) hcalloc(sizeof(struct param));
 | |
|     pm->node.nam = dupstring(name);
 | |
|     pm->node.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->node.nam);
 | |
|     else {
 | |
| 	pm->u.str = dupstring("");
 | |
| 	pm->node.flags |= (PM_UNSET|PM_SPECIAL);
 | |
|     }
 | |
|     return &pm->node;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| 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.node.flags = PM_SCALAR | PM_READONLY;
 | |
|     pm.gsu.s = &nullsetscalar_gsu;
 | |
| 
 | |
|     while (he) {
 | |
| 	if (func != scancountparams) {
 | |
| 	    convbase(buf, he->histnum, 10);
 | |
| 	    pm.node.nam = dupstring(buf);
 | |
| 	    if ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
 | |
| 		!(flags & SCANPM_WANTKEYS))
 | |
| 		pm.u.str = dupstring(he->node.nam);
 | |
| 	}
 | |
| 	func(&pm.node, flags);
 | |
| 
 | |
| 	he = up_histent(he);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* Function for the historywords special parameter. */
 | |
| 
 | |
| /**/
 | |
| static char **
 | |
| histwgetfn(UNUSED(Param pm))
 | |
| {
 | |
|     char *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, 0)))
 | |
|         for (n = firstnode(ll); n; incnode(n))
 | |
|             pushnode(l, getdata(n));
 | |
| 
 | |
|     while (he) {
 | |
| 	char *hstr = he->node.nam;
 | |
| 	int len = strlen(hstr);
 | |
| 	for (iw = he->nwords - 1; iw >= 0; iw--) {
 | |
| 	    int wbegin = he->words[iw * 2];
 | |
| 	    int wend = he->words[iw * 2 + 1];
 | |
| 
 | |
| 	    if (wbegin < 0 || wbegin >= len || wend < 0 || wend > len)
 | |
| 		break;
 | |
| 	    h = hstr + wbegin;
 | |
| 	    e = hstr + wend;
 | |
| 	    sav = *e;
 | |
| 	    *e = '\0';
 | |
| 	    addlinknode(l, dupstring(h));
 | |
| 	    *e = sav;
 | |
| 	}
 | |
| 	he = up_histent(he);
 | |
|     }
 | |
| 
 | |
|     return hlinklist2array(l, 0);
 | |
| }
 | |
| 
 | |
| /* Functions for the jobtexts special parameter. */
 | |
| 
 | |
| /**/
 | |
| static char *
 | |
| pmjobtext(Job jtab, int job)
 | |
| {
 | |
|     Process pn;
 | |
|     int len = 1;
 | |
|     char *ret;
 | |
| 
 | |
|     for (pn = jtab[job].procs; pn; pn = pn->next)
 | |
| 	len += strlen(pn->text) + 3;
 | |
| 
 | |
|     ret = (char *) zhalloc(len);
 | |
|     ret[0] = '\0';
 | |
| 
 | |
|     for (pn = jtab[job].procs; pn; pn = pn->next) {
 | |
| 	strcat(ret, pn->text);
 | |
| 	if (pn->next)
 | |
| 	    strcat(ret, " | ");
 | |
|     }
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static HashNode
 | |
| getpmjobtext(UNUSED(HashTable ht), const char *name)
 | |
| {
 | |
|     Param pm = NULL;
 | |
|     int job, jmax;
 | |
|     char *pend;
 | |
|     Job jtab;
 | |
| 
 | |
|     pm = (Param) hcalloc(sizeof(struct param));
 | |
|     pm->node.nam = dupstring(name);
 | |
|     pm->node.flags = PM_SCALAR | PM_READONLY;
 | |
|     pm->gsu.s = &nullsetscalar_gsu;
 | |
| 
 | |
|     selectjobtab(&jtab, &jmax);
 | |
| 
 | |
|     job = strtod(name, &pend);
 | |
|     /* Non-numeric keys are looked up by job name */
 | |
|     if (*pend)
 | |
| 	job = getjob(name, NULL);
 | |
|     if (job >= 1 && job <= jmax &&
 | |
| 	jtab[job].stat && jtab[job].procs &&
 | |
| 	!(jtab[job].stat & STAT_NOPRINT))
 | |
| 	pm->u.str = pmjobtext(jtab, job);
 | |
|     else {
 | |
| 	pm->u.str = dupstring("");
 | |
| 	pm->node.flags |= (PM_UNSET|PM_SPECIAL);
 | |
|     }
 | |
|     return &pm->node;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| scanpmjobtexts(UNUSED(HashTable ht), ScanFunc func, int flags)
 | |
| {
 | |
|     struct param pm;
 | |
|     int job, jmax;
 | |
|     char buf[40];
 | |
|     Job jtab;
 | |
| 
 | |
|     memset((void *)&pm, 0, sizeof(struct param));
 | |
|     pm.node.flags = PM_SCALAR | PM_READONLY;
 | |
|     pm.gsu.s = &nullsetscalar_gsu;
 | |
| 
 | |
|     selectjobtab(&jtab, &jmax);
 | |
| 
 | |
|     for (job = 1; job <= jmax; job++) {
 | |
| 	if (jtab[job].stat && jtab[job].procs &&
 | |
| 	    !(jtab[job].stat & STAT_NOPRINT)) {
 | |
| 	    if (func != scancountparams) {
 | |
| 		sprintf(buf, "%d", job);
 | |
| 		pm.node.nam = dupstring(buf);
 | |
| 		if ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
 | |
| 		    !(flags & SCANPM_WANTKEYS))
 | |
| 		    pm.u.str = pmjobtext(jtab, job);
 | |
| 	    }
 | |
| 	    func(&pm.node, flags);
 | |
| 	}
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* Functions for the jobstates special parameter. */
 | |
| 
 | |
| /**/
 | |
| static char *
 | |
| pmjobstate(Job jtab, int job)
 | |
| {
 | |
|     Process pn;
 | |
|     char buf[256], buf2[128], *ret, *state, *cp;
 | |
| 
 | |
|     if (job == curjob)
 | |
| 	cp = ":+";
 | |
|     else if (job == prevjob)
 | |
| 	cp = ":-";
 | |
|     else
 | |
| 	cp = ":";
 | |
| 
 | |
|     if (jtab[job].stat & STAT_DONE)
 | |
| 	ret = dyncat("done", cp);
 | |
|     else if (jtab[job].stat & STAT_STOPPED)
 | |
| 	ret = dyncat("suspended", cp);
 | |
|     else
 | |
| 	ret = dyncat("running", cp);
 | |
| 
 | |
|     for (pn = jtab[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), const char *name)
 | |
| {
 | |
|     Param pm = NULL;
 | |
|     int job, jmax;
 | |
|     char *pend;
 | |
|     Job jtab;
 | |
| 
 | |
|     pm = (Param) hcalloc(sizeof(struct param));
 | |
|     pm->node.nam = dupstring(name);
 | |
|     pm->node.flags = PM_SCALAR | PM_READONLY;
 | |
|     pm->gsu.s = &nullsetscalar_gsu;
 | |
| 
 | |
|     selectjobtab(&jtab, &jmax);
 | |
| 
 | |
|     job = strtod(name, &pend);
 | |
|     if (*pend)
 | |
| 	job = getjob(name, NULL);
 | |
|     if (job >= 1 && job <= jmax &&
 | |
| 	jtab[job].stat && jtab[job].procs &&
 | |
| 	!(jtab[job].stat & STAT_NOPRINT))
 | |
| 	pm->u.str = pmjobstate(jtab, job);
 | |
|     else {
 | |
| 	pm->u.str = dupstring("");
 | |
| 	pm->node.flags |= (PM_UNSET|PM_SPECIAL);
 | |
|     }
 | |
|     return &pm->node;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| scanpmjobstates(UNUSED(HashTable ht), ScanFunc func, int flags)
 | |
| {
 | |
|     struct param pm;
 | |
|     int job, jmax;
 | |
|     Job jtab;
 | |
|     char buf[40];
 | |
| 
 | |
|     selectjobtab(&jtab, &jmax);
 | |
| 
 | |
|     memset((void *)&pm, 0, sizeof(struct param));
 | |
|     pm.node.flags = PM_SCALAR | PM_READONLY;
 | |
|     pm.gsu.s = &nullsetscalar_gsu;
 | |
| 
 | |
|     for (job = 1; job <= jmax; job++) {
 | |
| 	if (jtab[job].stat && jtab[job].procs &&
 | |
| 	    !(jtab[job].stat & STAT_NOPRINT)) {
 | |
| 	    if (func != scancountparams) {
 | |
| 		sprintf(buf, "%d", job);
 | |
| 		pm.node.nam = dupstring(buf);
 | |
| 		if ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
 | |
| 		    !(flags & SCANPM_WANTKEYS))
 | |
| 		    pm.u.str = pmjobstate(jtab, job);
 | |
| 	    }
 | |
| 	    func(&pm.node, flags);
 | |
| 	}
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* Functions for the jobdirs special parameter. */
 | |
| 
 | |
| /**/
 | |
| static char *
 | |
| pmjobdir(Job jtab, int job)
 | |
| {
 | |
|     char *ret;
 | |
| 
 | |
|     ret = dupstring(jtab[job].pwd ? jtab[job].pwd : pwd);
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static HashNode
 | |
| getpmjobdir(UNUSED(HashTable ht), const char *name)
 | |
| {
 | |
|     Param pm = NULL;
 | |
|     int job, jmax;
 | |
|     char *pend;
 | |
|     Job jtab;
 | |
| 
 | |
|     pm = (Param) hcalloc(sizeof(struct param));
 | |
|     pm->node.nam = dupstring(name);
 | |
|     pm->node.flags = PM_SCALAR | PM_READONLY;
 | |
|     pm->gsu.s = &nullsetscalar_gsu;
 | |
| 
 | |
|     selectjobtab(&jtab, &jmax);
 | |
| 
 | |
|     job = strtod(name, &pend);
 | |
|     if (*pend)
 | |
| 	job = getjob(name, NULL);
 | |
|     if (job >= 1 && job <= jmax &&
 | |
| 	jtab[job].stat && jtab[job].procs &&
 | |
| 	!(jtab[job].stat & STAT_NOPRINT))
 | |
| 	pm->u.str = pmjobdir(jtab, job);
 | |
|     else {
 | |
| 	pm->u.str = dupstring("");
 | |
| 	pm->node.flags |= (PM_UNSET|PM_SPECIAL);
 | |
|     }
 | |
|     return &pm->node;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| scanpmjobdirs(UNUSED(HashTable ht), ScanFunc func, int flags)
 | |
| {
 | |
|     struct param pm;
 | |
|     int job, jmax;
 | |
|     char buf[40];
 | |
|     Job jtab;
 | |
| 
 | |
|     memset((void *)&pm, 0, sizeof(struct param));
 | |
|     pm.node.flags = PM_SCALAR | PM_READONLY;
 | |
|     pm.gsu.s = &nullsetscalar_gsu;
 | |
| 
 | |
|     selectjobtab(&jtab, &jmax);
 | |
| 
 | |
|     for (job = 1; job <= jmax; job++) {
 | |
|        if (jtab[job].stat && jtab[job].procs &&
 | |
|            !(jtab[job].stat & STAT_NOPRINT)) {
 | |
|            if (func != scancountparams) {
 | |
| 	       sprintf(buf, "%d", job);
 | |
| 	       pm.node.nam = dupstring(buf);
 | |
|                if ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
 | |
| 		   !(flags & SCANPM_WANTKEYS))
 | |
| 		   pm.u.str = pmjobdir(jtab, job);
 | |
| 	   }
 | |
|            func(&pm.node, flags);
 | |
|        }
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* Functions for the nameddirs special parameter. */
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| setpmnameddir(Param pm, char *value)
 | |
| {
 | |
|     if (!value)
 | |
| 	zwarn("invalid value: ''");
 | |
|     else {
 | |
| 	Nameddir nd = (Nameddir) zshcalloc(sizeof(*nd));
 | |
| 
 | |
| 	nd->node.flags = 0;
 | |
| 	nd->dir = value;
 | |
| 	nameddirtab->addnode(nameddirtab, ztrdup(pm->node.nam), nd);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| unsetpmnameddir(Param pm, UNUSED(int exp))
 | |
| {
 | |
|     HashNode hd = nameddirtab->removenode(nameddirtab, pm->node.nam);
 | |
| 
 | |
|     if (hd)
 | |
| 	nameddirtab->freenode(hd);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| setpmnameddirs(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)->node.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.flags = v.start = 0;
 | |
| 	    v.end = -1;
 | |
| 	    v.arr = NULL;
 | |
| 	    v.pm = (Param) hn;
 | |
| 
 | |
| 	    if (!(val = getstrvalue(&v)))
 | |
| 		zwarn("invalid value: ''");
 | |
| 	    else {
 | |
| 		Nameddir nd = (Nameddir) zshcalloc(sizeof(*nd));
 | |
| 
 | |
| 		nd->node.flags = 0;
 | |
| 		nd->dir = ztrdup(val);
 | |
| 		nameddirtab->addnode(nameddirtab, ztrdup(hn->nam), nd);
 | |
| 	    }
 | |
| 	}
 | |
| 
 | |
|     /* The INTERACTIVE stuff ensures that the dirs are not immediately removed
 | |
|      * when the sub-pms are deleted. */
 | |
| 
 | |
|     i = opts[INTERACTIVE];
 | |
|     opts[INTERACTIVE] = 0;
 | |
|     /* See setpmcommands() above */
 | |
|     if (ht != pm->u.hash)
 | |
| 	deleteparamtable(ht);
 | |
|     opts[INTERACTIVE] = i;
 | |
| }
 | |
| 
 | |
| static const struct gsu_scalar pmnamedir_gsu =
 | |
| { strgetfn, setpmnameddir, unsetpmnameddir };
 | |
| 
 | |
| /**/
 | |
| static HashNode
 | |
| getpmnameddir(UNUSED(HashTable ht), const char *name)
 | |
| {
 | |
|     Param pm = NULL;
 | |
|     Nameddir nd;
 | |
| 
 | |
|     pm = (Param) hcalloc(sizeof(struct param));
 | |
|     pm->node.nam = dupstring(name);
 | |
|     pm->node.flags = PM_SCALAR;
 | |
|     pm->gsu.s = &pmnamedir_gsu;
 | |
|     if ((nd = (Nameddir) nameddirtab->getnode(nameddirtab, name)) &&
 | |
| 	!(nd->node.flags & ND_USERNAME))
 | |
| 	pm->u.str = dupstring(nd->dir);
 | |
|     else {
 | |
| 	pm->u.str = dupstring("");
 | |
| 	pm->node.flags |= (PM_UNSET|PM_SPECIAL);
 | |
|     }
 | |
|     return &pm->node;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| 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.node.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)->node.flags & ND_USERNAME)) {
 | |
| 		pm.node.nam = hn->nam;
 | |
| 		if (func != scancountparams &&
 | |
| 		    ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
 | |
| 		     !(flags & SCANPM_WANTKEYS)))
 | |
| 		    pm.u.str = dupstring(nd->dir);
 | |
| 		func(&pm.node, flags);
 | |
| 	    }
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /* Functions for the userdirs special parameter. */
 | |
| 
 | |
| /**/
 | |
| static HashNode
 | |
| getpmuserdir(UNUSED(HashTable ht), const char *name)
 | |
| {
 | |
|     Param pm = NULL;
 | |
|     Nameddir nd;
 | |
| 
 | |
|     nameddirtab->filltable(nameddirtab);
 | |
| 
 | |
|     pm = (Param) hcalloc(sizeof(struct param));
 | |
|     pm->node.nam = dupstring(name);
 | |
|     pm->node.flags = PM_SCALAR | PM_READONLY;
 | |
|     pm->gsu.s = &nullsetscalar_gsu;
 | |
|     if ((nd = (Nameddir) nameddirtab->getnode(nameddirtab, name)) &&
 | |
| 	(nd->node.flags & ND_USERNAME))
 | |
| 	pm->u.str = dupstring(nd->dir);
 | |
|     else {
 | |
| 	pm->u.str = dupstring("");
 | |
| 	pm->node.flags |= (PM_UNSET|PM_SPECIAL);
 | |
|     }
 | |
|     return &pm->node;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| 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.node.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)->node.flags & ND_USERNAME) {
 | |
| 		pm.node.nam = hn->nam;
 | |
| 		if (func != scancountparams &&
 | |
| 		    ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
 | |
| 		     !(flags & SCANPM_WANTKEYS)))
 | |
| 		    pm.u.str = dupstring(nd->dir);
 | |
| 		func(&pm.node, 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->node.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->node.nam);
 | |
| 
 | |
|     if (hd)
 | |
| 	aliastab->freenode(hd);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| unsetpmsalias(Param pm, UNUSED(int exp))
 | |
| {
 | |
|     HashNode hd = sufaliastab->removenode(sufaliastab, pm->node.nam);
 | |
| 
 | |
|     if (hd)
 | |
| 	sufaliastab->freenode(hd);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| setaliases(HashTable alht, 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)->node.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.flags = 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));
 | |
| 	}
 | |
|     /* See setpmcommands() above */
 | |
|     if (ht != pm->u.hash)
 | |
| 	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->node.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), const char *name, int flags)
 | |
| {
 | |
|     Param pm = NULL;
 | |
|     Alias al;
 | |
| 
 | |
|     pm = (Param) hcalloc(sizeof(struct param));
 | |
|     pm->node.nam = dupstring(name);
 | |
| 
 | |
|     assignaliasdefs(pm, flags);
 | |
| 
 | |
|     if ((al = (Alias) alht->getnode2(alht, name)) &&
 | |
| 	flags == al->node.flags)
 | |
| 	pm->u.str = dupstring(al->text);
 | |
|     else {
 | |
| 	pm->u.str = dupstring("");
 | |
| 	pm->node.flags |= (PM_UNSET|PM_SPECIAL);
 | |
|     }
 | |
|     return &pm->node;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static HashNode
 | |
| getpmralias(HashTable ht, const char *name)
 | |
| {
 | |
|     return getalias(aliastab, ht, name, 0);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static HashNode
 | |
| getpmdisralias(HashTable ht, const char *name)
 | |
| {
 | |
|     return getalias(aliastab, ht, name, DISABLED);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static HashNode
 | |
| getpmgalias(HashTable ht, const char *name)
 | |
| {
 | |
|     return getalias(aliastab, ht, name, ALIAS_GLOBAL);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static HashNode
 | |
| getpmdisgalias(HashTable ht, const char *name)
 | |
| {
 | |
|     return getalias(aliastab, ht, name, ALIAS_GLOBAL|DISABLED);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static HashNode
 | |
| getpmsalias(HashTable ht, const char *name)
 | |
| {
 | |
|     return getalias(sufaliastab, ht, name, ALIAS_SUFFIX);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static HashNode
 | |
| getpmdissalias(HashTable ht, const 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->node.next) {
 | |
| 	    if (alflags == al->node.flags) {
 | |
| 		pm.node.nam = al->node.nam;
 | |
| 		if (func != scancountparams &&
 | |
| 		    ((pmflags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
 | |
| 		     !(pmflags & SCANPM_WANTKEYS)))
 | |
| 		    pm.u.str = dupstring(al->text);
 | |
| 		func(&pm.node, 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);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Functions for the usergroups special parameter */
 | |
| 
 | |
| /*
 | |
|  * Get GID and names for groups of which the current user is a member.
 | |
|  */
 | |
| 
 | |
| /**/
 | |
| static Groupset get_all_groups(void)
 | |
| {
 | |
| #ifdef DISABLE_DYNAMIC_NSS
 | |
|     return NULL;
 | |
| #else
 | |
|     Groupset gs = zhalloc(sizeof(*gs));
 | |
|     Groupmap gaptr;
 | |
|     gid_t *list, *lptr, egid;
 | |
|     int add_egid;
 | |
|     struct group *grptr;
 | |
| 
 | |
|     egid = getegid();
 | |
|     add_egid = 1;
 | |
|     gs->num = getgroups(0, NULL);
 | |
|     if (gs->num > 0) {
 | |
| 	list = zhalloc(gs->num * sizeof(*list));
 | |
| 	if (getgroups(gs->num, list) < 0) {
 | |
| 	    return NULL;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * It's unspecified whether $EGID is included in the
 | |
| 	 * group set, so check.
 | |
| 	 */
 | |
| 	for (lptr = list; lptr < list + gs->num; lptr++) {
 | |
| 	    if (*lptr == egid) {
 | |
| 		add_egid = 0;
 | |
| 		break;
 | |
| 	    }
 | |
| 	}
 | |
| 	gs->array = zhalloc((gs->num + add_egid) * sizeof(*gs->array));
 | |
| 	/* Put EGID if needed first */
 | |
| 	gaptr = gs->array + add_egid;
 | |
| 	for (lptr = list; lptr < list + gs->num; lptr++) {
 | |
| 	    gaptr->gid = *lptr;
 | |
| 	    gaptr++;
 | |
| 	}
 | |
| 	gs->num += add_egid;
 | |
|     } else {
 | |
| 	/* Just use effective GID */
 | |
| 	gs->num = 1;
 | |
| 	gs->array = zhalloc(sizeof(*gs->array));
 | |
|     }
 | |
|     if (add_egid) {
 | |
| 	gs->array->gid = egid;
 | |
|     }
 | |
| 
 | |
|     /* Get group names */
 | |
|     for (gaptr = gs->array; gaptr < gs->array + gs->num; gaptr++) {
 | |
| 	grptr = getgrgid(gaptr->gid);
 | |
| 	if (!grptr) {
 | |
| 	    return NULL;
 | |
| 	}
 | |
| 	gaptr->name = dupstring(grptr->gr_name);
 | |
|     }
 | |
| 
 | |
|     return gs;
 | |
| #endif /* DISABLE_DYNAMIC_NSS */
 | |
| }
 | |
| 
 | |
| /* Standard hash element lookup. */
 | |
| 
 | |
| /**/
 | |
| static HashNode
 | |
| getpmusergroups(UNUSED(HashTable ht), const char *name)
 | |
| {
 | |
|     Param pm = NULL;
 | |
|     Groupset gs = get_all_groups();
 | |
|     Groupmap gaptr;
 | |
| 
 | |
|     pm = (Param)hcalloc(sizeof(struct param));
 | |
|     pm->node.nam = dupstring(name);
 | |
|     pm->node.flags = PM_SCALAR | PM_READONLY;
 | |
|     pm->gsu.s = &nullsetscalar_gsu;
 | |
| 
 | |
|     if (!gs) {
 | |
| #ifdef DISABLE_DYNAMIC_NSS
 | |
| 	zerr("parameter 'usergroups' not available: NSS is disabled");
 | |
| #else
 | |
| 	zerr("failed to retrieve groups for user: %e", errno);
 | |
| #endif
 | |
| 	pm->u.str = dupstring("");
 | |
| 	pm->node.flags |= (PM_UNSET|PM_SPECIAL);
 | |
| 	return &pm->node;
 | |
|     }
 | |
| 
 | |
|     for (gaptr = gs->array; gaptr < gs->array + gs->num; gaptr++) {
 | |
| 	if (!strcmp(name, gaptr->name)) {
 | |
| 	    char buf[DIGBUFSIZE];
 | |
| 
 | |
| 	    sprintf(buf, "%d", (int)gaptr->gid);
 | |
| 	    pm->u.str = dupstring(buf);
 | |
| 	    return &pm->node;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     pm->u.str = dupstring("");
 | |
|     pm->node.flags |= (PM_UNSET|PM_SPECIAL);
 | |
|     return &pm->node;
 | |
| }
 | |
| 
 | |
| /* Standard hash scan. */
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| scanpmusergroups(UNUSED(HashTable ht), ScanFunc func, int flags)
 | |
| {
 | |
|     struct param pm;
 | |
|     Groupset gs = get_all_groups();
 | |
|     Groupmap gaptr;
 | |
| 
 | |
|     if (!gs) {
 | |
| #ifdef DISABLE_DYNAMIC_NSS
 | |
| 	zerr("parameter 'usergroups' not available: NSS is disabled");
 | |
| #else
 | |
| 	zerr("failed to retrieve groups for user: %e", errno);
 | |
| #endif
 | |
| 	return;
 | |
|     }
 | |
| 
 | |
|     memset((void *)&pm, 0, sizeof(pm));
 | |
|     pm.node.flags = PM_SCALAR | PM_READONLY;
 | |
|     pm.gsu.s = &nullsetscalar_gsu;
 | |
| 
 | |
|     for (gaptr = gs->array; gaptr < gs->array + gs->num; gaptr++) {
 | |
| 	pm.node.nam = gaptr->name;
 | |
| 	if (func != scancountparams &&
 | |
| 	    ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
 | |
| 	     !(flags & SCANPM_WANTKEYS))) {
 | |
| 	    char buf[DIGBUFSIZE];
 | |
| 
 | |
| 	    sprintf(buf, "%d", (int)gaptr->gid);
 | |
| 	    pm.u.str = dupstring(buf);
 | |
| 	}
 | |
| 	func(&pm.node, flags);
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Table for defined parameters. */
 | |
| 
 | |
| struct pardef {
 | |
|     char *name;
 | |
|     int flags;
 | |
|     GetNodeFunc getnfn;
 | |
|     ScanTabFunc scantfn;
 | |
|     GsuHash hash_gsu;
 | |
|     GsuArray array_gsu;
 | |
|     Param pm;
 | |
| };
 | |
| 
 | |
| 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 functrace_gsu =
 | |
| { functracegetfn, arrsetfn, stdunsetfn };
 | |
| static const struct gsu_array funcsourcetrace_gsu =
 | |
| { funcsourcetracegetfn, arrsetfn, stdunsetfn };
 | |
| static const struct gsu_array funcfiletrace_gsu =
 | |
| { funcfiletracegetfn, 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 patchars_gsu =
 | |
| { patcharsgetfn, arrsetfn, stdunsetfn };
 | |
| static const struct gsu_array dispatchars_gsu =
 | |
| { dispatcharsgetfn, arrsetfn, stdunsetfn };
 | |
| static const struct gsu_array dirs_gsu =
 | |
| { dirsgetfn, dirssetfn, stdunsetfn };
 | |
| static const struct gsu_array historywords_gsu =
 | |
| { histwgetfn, arrsetfn, stdunsetfn };
 | |
| 
 | |
| /* Make sure to update autofeatures in parameter.mdd if necessary */
 | |
| static struct paramdef partab[] = {
 | |
|     SPECIALPMDEF("aliases", 0,
 | |
| 	    &pmraliases_gsu, getpmralias, scanpmraliases),
 | |
|     SPECIALPMDEF("builtins", PM_READONLY_SPECIAL, NULL, getpmbuiltin, scanpmbuiltins),
 | |
|     SPECIALPMDEF("commands", 0, &pmcommands_gsu, getpmcommand, scanpmcommands),
 | |
|     SPECIALPMDEF("dirstack", PM_ARRAY,
 | |
| 	    &dirs_gsu, NULL, NULL),
 | |
|     SPECIALPMDEF("dis_aliases", 0,
 | |
| 	    &pmdisraliases_gsu, getpmdisralias, scanpmdisraliases),
 | |
|     SPECIALPMDEF("dis_builtins", PM_READONLY_SPECIAL,
 | |
| 	    NULL, getpmdisbuiltin, scanpmdisbuiltins),
 | |
|     SPECIALPMDEF("dis_functions", 0, 
 | |
| 	    &pmdisfunctions_gsu, getpmdisfunction, scanpmdisfunctions),
 | |
|     SPECIALPMDEF("dis_functions_source", PM_READONLY_SPECIAL, NULL,
 | |
| 		 getpmdisfunction_source, scanpmdisfunction_source),
 | |
|     SPECIALPMDEF("dis_galiases", 0,
 | |
| 	    &pmdisgaliases_gsu, getpmdisgalias, scanpmdisgaliases),
 | |
|     SPECIALPMDEF("dis_patchars", PM_ARRAY|PM_READONLY_SPECIAL,
 | |
| 	    &dispatchars_gsu, NULL, NULL),
 | |
|     SPECIALPMDEF("dis_reswords", PM_ARRAY|PM_READONLY_SPECIAL,
 | |
| 	    &disreswords_gsu, NULL, NULL),
 | |
|     SPECIALPMDEF("dis_saliases", 0,
 | |
| 	    &pmdissaliases_gsu, getpmdissalias, scanpmdissaliases),
 | |
|     SPECIALPMDEF("funcfiletrace", PM_ARRAY|PM_READONLY_SPECIAL,
 | |
| 	    &funcfiletrace_gsu, NULL, NULL),
 | |
|     SPECIALPMDEF("funcsourcetrace", PM_ARRAY|PM_READONLY_SPECIAL,
 | |
| 	    &funcsourcetrace_gsu, NULL, NULL),
 | |
|     SPECIALPMDEF("funcstack", PM_ARRAY|PM_READONLY_SPECIAL,
 | |
| 	    &funcstack_gsu, NULL, NULL),
 | |
|     SPECIALPMDEF("functions", 0, &pmfunctions_gsu, getpmfunction,
 | |
| 		 scanpmfunctions),
 | |
|     SPECIALPMDEF("functions_source", PM_READONLY_SPECIAL, NULL,
 | |
| 		 getpmfunction_source, scanpmfunction_source),
 | |
|     SPECIALPMDEF("functrace", PM_ARRAY|PM_READONLY_SPECIAL,
 | |
| 	    &functrace_gsu, NULL, NULL),
 | |
|     SPECIALPMDEF("galiases", 0,
 | |
| 	    &pmgaliases_gsu, getpmgalias, scanpmgaliases),
 | |
|     SPECIALPMDEF("history", PM_READONLY_SPECIAL,
 | |
| 	    NULL, getpmhistory, scanpmhistory),
 | |
|     SPECIALPMDEF("historywords", PM_ARRAY|PM_READONLY_SPECIAL,
 | |
| 	    &historywords_gsu, NULL, NULL),
 | |
|     SPECIALPMDEF("jobdirs", PM_READONLY_SPECIAL,
 | |
| 	    NULL, getpmjobdir, scanpmjobdirs),
 | |
|     SPECIALPMDEF("jobstates", PM_READONLY_SPECIAL,
 | |
| 	    NULL, getpmjobstate, scanpmjobstates),
 | |
|     SPECIALPMDEF("jobtexts", PM_READONLY_SPECIAL,
 | |
| 	    NULL, getpmjobtext, scanpmjobtexts),
 | |
|     SPECIALPMDEF("modules", PM_READONLY_SPECIAL,
 | |
| 	    NULL, getpmmodule, scanpmmodules),
 | |
|     SPECIALPMDEF("nameddirs", 0,
 | |
| 	    &pmnameddirs_gsu, getpmnameddir, scanpmnameddirs),
 | |
|     SPECIALPMDEF("options", 0,
 | |
| 	    &pmoptions_gsu, getpmoption, scanpmoptions),
 | |
|     SPECIALPMDEF("parameters", PM_READONLY_SPECIAL,
 | |
| 	    NULL, getpmparameter, scanpmparameters),
 | |
|     SPECIALPMDEF("patchars", PM_ARRAY|PM_READONLY_SPECIAL,
 | |
| 	    &patchars_gsu, NULL, NULL),
 | |
|     SPECIALPMDEF("reswords", PM_ARRAY|PM_READONLY_SPECIAL,
 | |
| 	    &reswords_gsu, NULL, NULL),
 | |
|     SPECIALPMDEF("saliases", 0,
 | |
| 	    &pmsaliases_gsu, getpmsalias, scanpmsaliases),
 | |
|     SPECIALPMDEF("userdirs", PM_READONLY_SPECIAL,
 | |
| 	    NULL, getpmuserdir, scanpmuserdirs),
 | |
|     SPECIALPMDEF("usergroups", PM_READONLY_SPECIAL,
 | |
| 	    NULL, getpmusergroups, scanpmusergroups)
 | |
| };
 | |
| 
 | |
| static struct features module_features = {
 | |
|     NULL, 0,
 | |
|     NULL, 0,
 | |
|     NULL, 0,
 | |
|     partab, sizeof(partab)/sizeof(*partab),
 | |
|     0
 | |
| };
 | |
| 
 | |
| /**/
 | |
| int
 | |
| setup_(UNUSED(Module m))
 | |
| {
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| features_(Module m, char ***features)
 | |
| {
 | |
|     *features = featuresarray(m, &module_features);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| enables_(Module m, int **enables)
 | |
| {
 | |
|     int ret;
 | |
|     /*
 | |
|      * If we remove features, we shouldn't have an effect
 | |
|      * on the main shell, so set the flag to indicate.
 | |
|      */
 | |
|     incleanup = 1;
 | |
|     ret = handlefeatures(m, &module_features, enables);
 | |
|     incleanup = 0;
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| boot_(UNUSED(Module m))
 | |
| {
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| cleanup_(Module m)
 | |
| {
 | |
|     int ret;
 | |
|     incleanup = 1;
 | |
|     ret = setfeatureenables(m, &module_features, NULL);
 | |
|     incleanup = 0;
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| finish_(UNUSED(Module m))
 | |
| {
 | |
|     return 0;
 | |
| }
 |