1/*
2 * parameter.c - parameter interface to zsh internals
3 *
4 * This file is part of zsh, the Z shell.
5 *
6 * Copyright (c) 1999 Sven Wischnowsky
7 * All rights reserved.
8 *
9 * Permission is hereby granted, without written agreement and without
10 * license or royalty fees, to use, copy, modify, and distribute this
11 * software and to distribute modified versions of this software for any
12 * purpose, provided that the above copyright notice and the following
13 * two paragraphs appear in all copies of this software.
14 *
15 * In no event shall Sven Wischnowsky or the Zsh Development Group be liable
16 * to any party for direct, indirect, special, incidental, or consequential
17 * damages arising out of the use of this software and its documentation,
18 * even if Sven Wischnowsky and the Zsh Development Group have been advised of
19 * the possibility of such damage.
20 *
21 * Sven Wischnowsky and the Zsh Development Group specifically disclaim any
22 * warranties, including, but not limited to, the implied warranties of
23 * merchantability and fitness for a particular purpose.  The software
24 * provided hereunder is on an "as is" basis, and Sven Wischnowsky and the
25 * Zsh Development Group have no obligation to provide maintenance,
26 * support, updates, enhancements, or modifications.
27 *
28 */
29
30#include "parameter.mdh"
31#include "parameter.pro"
32
33/* This says if we are cleaning up when the module is unloaded. */
34
35static int incleanup;
36
37/* Functions for the parameters special parameter. */
38
39/* Return a string describing the type of a parameter. */
40
41/**/
42static char *
43paramtypestr(Param pm)
44{
45    char *val = NULL;
46    int f = pm->node.flags;
47
48    if (!(f & PM_UNSET)) {
49	if (pm->node.flags & PM_AUTOLOAD)
50	    return dupstring("undefined");
51
52	switch (PM_TYPE(f)) {
53	case PM_SCALAR:  val = "scalar"; break;
54	case PM_ARRAY:   val = "array"; break;
55	case PM_INTEGER: val = "integer"; break;
56	case PM_EFLOAT:
57	case PM_FFLOAT:  val = "float"; break;
58	case PM_HASHED:  val = "association"; break;
59	}
60	DPUTS(!val, "BUG: type not handled in parameter");
61	val = dupstring(val);
62	if (pm->level)
63	    val = dyncat(val, "-local");
64	if (f & PM_LEFT)
65	    val = dyncat(val, "-left");
66	if (f & PM_RIGHT_B)
67	    val = dyncat(val, "-right_blanks");
68	if (f & PM_RIGHT_Z)
69	    val = dyncat(val, "-right_zeros");
70	if (f & PM_LOWER)
71	    val = dyncat(val, "-lower");
72	if (f & PM_UPPER)
73	    val = dyncat(val, "-upper");
74	if (f & PM_READONLY)
75	    val = dyncat(val, "-readonly");
76	if (f & PM_TAGGED)
77	    val = dyncat(val, "-tag");
78	if (f & PM_EXPORTED)
79	    val = dyncat(val, "-export");
80	if (f & PM_UNIQUE)
81	    val = dyncat(val, "-unique");
82	if (f & PM_HIDE)
83	    val = dyncat(val, "-hide");
84	if (f & PM_HIDEVAL)
85	    val = dyncat(val, "-hideval");
86	if (f & PM_SPECIAL)
87	    val = dyncat(val, "-special");
88    } else
89	val = dupstring("");
90
91    return val;
92}
93
94/**/
95static HashNode
96getpmparameter(UNUSED(HashTable ht), const char *name)
97{
98    Param rpm, pm = NULL;
99
100    pm = (Param) hcalloc(sizeof(struct param));
101    pm->node.nam = dupstring(name);
102    pm->node.flags = PM_SCALAR | PM_READONLY;
103    pm->gsu.s = &nullsetscalar_gsu;
104    if ((rpm = (Param) realparamtab->getnode(realparamtab, name)) &&
105	!(rpm->node.flags & PM_UNSET))
106	pm->u.str = paramtypestr(rpm);
107    else {
108	pm->u.str = dupstring("");
109	pm->node.flags |= PM_UNSET;
110    }
111    return &pm->node;
112}
113
114/**/
115static void
116scanpmparameters(UNUSED(HashTable ht), ScanFunc func, int flags)
117{
118    struct param pm;
119    int i;
120    HashNode hn;
121
122    memset((void *)&pm, 0, sizeof(struct param));
123    pm.node.flags = PM_SCALAR | PM_READONLY;
124    pm.gsu.s = &nullsetscalar_gsu;
125
126    for (i = 0; i < realparamtab->hsize; i++)
127	for (hn = realparamtab->nodes[i]; hn; hn = hn->next) {
128	    if (((Param)hn)->node.flags & PM_UNSET)
129		continue;
130	    pm.node.nam = hn->nam;
131	    if (func != scancountparams &&
132		((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
133		 !(flags & SCANPM_WANTKEYS)))
134		pm.u.str = paramtypestr((Param) hn);
135	    func(&pm.node, flags);
136	}
137}
138
139/* Functions for the commands special parameter. */
140
141/**/
142static void
143setpmcommand(Param pm, char *value)
144{
145    if (isset(RESTRICTED)) {
146	zwarn("restricted: %s", value);
147	zsfree(value);
148    } else {
149	Cmdnam cn = zshcalloc(sizeof(*cn));
150
151	cn->node.flags = HASHED;
152	cn->u.cmd = value;
153
154	cmdnamtab->addnode(cmdnamtab, ztrdup(pm->node.nam), &cn->node);
155    }
156}
157
158/**/
159static void
160unsetpmcommand(Param pm, UNUSED(int exp))
161{
162    HashNode hn = cmdnamtab->removenode(cmdnamtab, pm->node.nam);
163
164    if (hn)
165	cmdnamtab->freenode(hn);
166}
167
168/**/
169static void
170setpmcommands(UNUSED(Param pm), HashTable ht)
171{
172    int i;
173    HashNode hn;
174
175    if (!ht)
176	return;
177
178    for (i = 0; i < ht->hsize; i++)
179	for (hn = ht->nodes[i]; hn; hn = hn->next) {
180	    Cmdnam cn = zshcalloc(sizeof(*cn));
181	    struct value v;
182
183	    v.isarr = v.flags = v.start = 0;
184	    v.end = -1;
185	    v.arr = NULL;
186	    v.pm = (Param) hn;
187
188	    cn->node.flags = HASHED;
189	    cn->u.cmd = ztrdup(getstrvalue(&v));
190
191	    cmdnamtab->addnode(cmdnamtab, ztrdup(hn->nam), &cn->node);
192	}
193    deleteparamtable(ht);
194}
195
196static const struct gsu_scalar pmcommand_gsu =
197{ strgetfn, setpmcommand, unsetpmcommand };
198
199
200/**/
201static HashNode
202getpmcommand(UNUSED(HashTable ht), const char *name)
203{
204    Cmdnam cmd;
205    Param pm = NULL;
206
207    if (!(cmd = (Cmdnam) cmdnamtab->getnode(cmdnamtab, name)) &&
208	isset(HASHLISTALL)) {
209	cmdnamtab->filltable(cmdnamtab);
210	cmd = (Cmdnam) cmdnamtab->getnode(cmdnamtab, name);
211    }
212    pm = (Param) hcalloc(sizeof(struct param));
213    pm->node.nam = dupstring(name);
214    pm->node.flags = PM_SCALAR;
215    pm->gsu.s = &pmcommand_gsu;
216    if (cmd) {
217	if (cmd->node.flags & HASHED)
218	    pm->u.str = cmd->u.cmd;
219	else {
220	    pm->u.str = zhalloc(strlen(*(cmd->u.name)) + strlen(name) + 2);
221	    strcpy(pm->u.str, *(cmd->u.name));
222	    strcat(pm->u.str, "/");
223	    strcat(pm->u.str, name);
224	}
225    } else {
226	pm->u.str = dupstring("");
227	pm->node.flags |= PM_UNSET;
228    }
229    return &pm->node;
230}
231
232/**/
233static void
234scanpmcommands(UNUSED(HashTable ht), ScanFunc func, int flags)
235{
236    struct param pm;
237    int i;
238    HashNode hn;
239    Cmdnam cmd;
240
241    if (isset(HASHLISTALL))
242	cmdnamtab->filltable(cmdnamtab);
243
244    memset((void *)&pm, 0, sizeof(struct param));
245    pm.node.flags = PM_SCALAR;
246    pm.gsu.s = &pmcommand_gsu;
247
248    for (i = 0; i < cmdnamtab->hsize; i++)
249	for (hn = cmdnamtab->nodes[i]; hn; hn = hn->next) {
250	    pm.node.nam = hn->nam;
251	    cmd = (Cmdnam) hn;
252	    if (func != scancountparams &&
253		((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
254		 !(flags & SCANPM_WANTKEYS))) {
255		if (cmd->node.flags & HASHED)
256		    pm.u.str = cmd->u.cmd;
257		else {
258		    pm.u.str = zhalloc(strlen(*(cmd->u.name)) +
259				       strlen(cmd->node.nam) + 2);
260		    strcpy(pm.u.str, *(cmd->u.name));
261		    strcat(pm.u.str, "/");
262		    strcat(pm.u.str, cmd->node.nam);
263		}
264	    }
265	    func(&pm.node, flags);
266	}
267}
268
269/* Functions for the functions special parameter. */
270
271/**/
272static void
273setfunction(char *name, char *val, int dis)
274{
275    char *value = dupstring(val);
276    Shfunc shf;
277    Eprog prog;
278    int sn;
279
280    val = metafy(val, strlen(val), META_REALLOC);
281
282    prog = parse_string(val, 1);
283
284    if (!prog || prog == &dummy_eprog) {
285	zwarn("invalid function definition", value);
286	zsfree(val);
287	return;
288    }
289    shf = (Shfunc) zshcalloc(sizeof(*shf));
290    shf->funcdef = dupeprog(prog, 0);
291    shf->node.flags = dis;
292    shfunc_set_sticky(shf);
293
294    if (!strncmp(name, "TRAP", 4) &&
295	(sn = getsignum(name + 4)) != -1) {
296	if (settrap(sn, NULL, ZSIG_FUNC)) {
297	    freeeprog(shf->funcdef);
298	    zfree(shf, sizeof(*shf));
299	    zsfree(val);
300	    return;
301	}
302    }
303    shfunctab->addnode(shfunctab, ztrdup(name), shf);
304    zsfree(val);
305}
306
307/**/
308static void
309setpmfunction(Param pm, char *value)
310{
311    setfunction(pm->node.nam, value, 0);
312}
313
314/**/
315static void
316setpmdisfunction(Param pm, char *value)
317{
318    setfunction(pm->node.nam, value, DISABLED);
319}
320
321/**/
322static void
323unsetpmfunction(Param pm, UNUSED(int exp))
324{
325    HashNode hn = shfunctab->removenode(shfunctab, pm->node.nam);
326
327    if (hn)
328	shfunctab->freenode(hn);
329}
330
331/**/
332static void
333setfunctions(UNUSED(Param pm), HashTable ht, int dis)
334{
335    int i;
336    HashNode hn;
337
338    if (!ht)
339	return;
340
341    for (i = 0; i < ht->hsize; i++)
342	for (hn = ht->nodes[i]; hn; hn = hn->next) {
343	    struct value v;
344
345	    v.isarr = v.flags = v.start = 0;
346	    v.end = -1;
347	    v.arr = NULL;
348	    v.pm = (Param) hn;
349
350	    setfunction(hn->nam, ztrdup(getstrvalue(&v)), dis);
351	}
352    deleteparamtable(ht);
353}
354
355/**/
356static void
357setpmfunctions(Param pm, HashTable ht)
358{
359    setfunctions(pm, ht, 0);
360}
361
362/**/
363static void
364setpmdisfunctions(Param pm, HashTable ht)
365{
366    setfunctions(pm, ht, DISABLED);
367}
368
369static const struct gsu_scalar pmfunction_gsu =
370{ strgetfn, setpmfunction, unsetpmfunction };
371static const struct gsu_scalar pmdisfunction_gsu =
372{ strgetfn, setpmdisfunction, unsetpmfunction };
373
374/**/
375static HashNode
376getfunction(UNUSED(HashTable ht), const char *name, int dis)
377{
378    Shfunc shf;
379    Param pm = NULL;
380
381    pm = (Param) hcalloc(sizeof(struct param));
382    pm->node.nam = dupstring(name);
383    pm->node.flags = PM_SCALAR;
384    pm->gsu.s = dis ? &pmdisfunction_gsu :  &pmfunction_gsu;
385
386    if ((shf = (Shfunc) shfunctab->getnode2(shfunctab, name)) &&
387	(dis ? (shf->node.flags & DISABLED) : !(shf->node.flags & DISABLED))) {
388	if (shf->node.flags & PM_UNDEFINED) {
389	    pm->u.str = dyncat("builtin autoload -X",
390			       ((shf->node.flags & PM_UNALIASED) ?
391				((shf->node.flags & PM_TAGGED) ? "Ut" : "U") :
392				((shf->node.flags & PM_TAGGED) ? "t" : "")));
393	} else {
394	    char *t = getpermtext(shf->funcdef, NULL, 1), *n, *h;
395
396	    if (shf->funcdef->flags & EF_RUN) {
397		n = nicedupstring(name);
398		h = (char *) zhalloc(strlen(t) + strlen(n) + 9);
399		h[0] = '\t';
400		strcpy(h + 1, t);
401		strcat(h, "\n\t");
402		strcat(h, n);
403		strcat(h, " \"$@\"");
404	    } else
405		h = dyncat("\t", t);
406	    zsfree(t);
407	    unmetafy(h, NULL);
408
409	    pm->u.str = h;
410	}
411    } else {
412	pm->u.str = dupstring("");
413	pm->node.flags |= PM_UNSET;
414    }
415    return &pm->node;
416}
417
418/**/
419static HashNode
420getpmfunction(HashTable ht, const char *name)
421{
422    return getfunction(ht, name, 0);
423}
424
425/**/
426static HashNode
427getpmdisfunction(HashTable ht, const char *name)
428{
429    return getfunction(ht, name, DISABLED);
430}
431
432/**/
433static void
434scanfunctions(UNUSED(HashTable ht), ScanFunc func, int flags, int dis)
435{
436    struct param pm;
437    int i;
438    HashNode hn;
439
440    memset((void *)&pm, 0, sizeof(struct param));
441    pm.node.flags = PM_SCALAR;
442    pm.gsu.s = dis ? &pmdisfunction_gsu : &pmfunction_gsu;
443
444    for (i = 0; i < shfunctab->hsize; i++)
445	for (hn = shfunctab->nodes[i]; hn; hn = hn->next) {
446	    if (dis ? (hn->flags & DISABLED) : !(hn->flags & DISABLED)) {
447		pm.node.nam = hn->nam;
448		if (func != scancountparams &&
449		    ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
450		     !(flags & SCANPM_WANTKEYS))) {
451		    if (((Shfunc) hn)->node.flags & PM_UNDEFINED) {
452			Shfunc shf = (Shfunc) hn;
453			pm.u.str =
454			    dyncat("builtin autoload -X",
455				   ((shf->node.flags & PM_UNALIASED) ?
456				    ((shf->node.flags & PM_TAGGED) ? "Ut" : "U") :
457				    ((shf->node.flags & PM_TAGGED) ? "t" : "")));
458		    } else {
459			char *t = getpermtext(((Shfunc) hn)->funcdef, NULL, 1);
460			char *n;
461
462			if (((Shfunc) hn)->funcdef->flags & EF_RUN) {
463			    n = nicedupstring(hn->nam);
464			    pm.u.str = (char *) zhalloc(strlen(t) + strlen(n) + 9);
465			    pm.u.str[0] = '\t';
466			    strcpy(pm.u.str + 1, t);
467			    strcat(pm.u.str, "\n\t");
468			    strcat(pm.u.str, n);
469			    strcat(pm.u.str, " \"$@\"");
470			} else
471			    pm.u.str = dyncat("\t", t);
472			unmetafy(pm.u.str, NULL);
473			zsfree(t);
474		    }
475		}
476		func(&pm.node, flags);
477	    }
478	}
479}
480
481/**/
482static void
483scanpmfunctions(HashTable ht, ScanFunc func, int flags)
484{
485    scanfunctions(ht, func, flags, 0);
486}
487
488/**/
489static void
490scanpmdisfunctions(HashTable ht, ScanFunc func, int flags)
491{
492    scanfunctions(ht, func, flags, DISABLED);
493}
494
495/* Functions for the funcstack special parameter. */
496
497/**/
498static char **
499funcstackgetfn(UNUSED(Param pm))
500{
501    Funcstack f;
502    int num;
503    char **ret, **p;
504
505    for (f = funcstack, num = 0; f; f = f->prev, num++);
506
507    ret = (char **) zhalloc((num + 1) * sizeof(char *));
508
509    for (f = funcstack, p = ret; f; f = f->prev, p++)
510	*p = f->name;
511    *p = NULL;
512
513    return ret;
514}
515
516/* Functions for the functrace special parameter. */
517
518/**/
519static char **
520functracegetfn(UNUSED(Param pm))
521{
522    Funcstack f;
523    int num;
524    char **ret, **p;
525
526    for (f = funcstack, num = 0; f; f = f->prev, num++);
527
528    ret = (char **) zhalloc((num + 1) * sizeof(char *));
529
530    for (f = funcstack, p = ret; f; f = f->prev, p++) {
531	char *colonpair;
532
533	colonpair = zhalloc(strlen(f->caller) + (f->lineno > 9999 ? 24 : 6));
534#if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD)
535	sprintf(colonpair, "%s:%lld", f->caller, f->lineno);
536#else
537	sprintf(colonpair, "%s:%ld", f->caller, (long)f->lineno);
538#endif
539
540	*p = colonpair;
541    }
542    *p = NULL;
543
544    return ret;
545}
546
547/* Functions for the funcsourcetrace special parameter. */
548
549/**/
550static char **
551funcsourcetracegetfn(UNUSED(Param pm))
552{
553    Funcstack f;
554    int num;
555    char **ret, **p;
556
557    for (f = funcstack, num = 0; f; f = f->prev, num++);
558
559    ret = (char **) zhalloc((num + 1) * sizeof(char *));
560
561    for (f = funcstack, p = ret; f; f = f->prev, p++) {
562	char *colonpair;
563	char *fname = f->filename ? f->filename : "";
564
565	colonpair = zhalloc(strlen(fname) + (f->flineno > 9999 ? 24 : 6));
566#if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD)
567	sprintf(colonpair, "%s:%lld", fname, f->flineno);
568#else
569	sprintf(colonpair, "%s:%ld", fname, (long)f->flineno);
570#endif
571
572	*p = colonpair;
573    }
574    *p = NULL;
575
576    return ret;
577}
578
579/* Functions for the funcfiletrace special parameter. */
580
581/**/
582static char **
583funcfiletracegetfn(UNUSED(Param pm))
584{
585    Funcstack f;
586    int num;
587    char **ret, **p;
588
589    for (f = funcstack, num = 0; f; f = f->prev, num++);
590
591    ret = (char **) zhalloc((num + 1) * sizeof(char *));
592
593    for (f = funcstack, p = ret; f; f = f->prev, p++) {
594	char *colonpair, *fname;
595
596	if (!f->prev || f->prev->tp == FS_SOURCE) {
597	    /*
598	     * Calling context is a file---either the parent
599	     * script or interactive shell, or a sourced
600	     * script.  Just print the file information for the caller
601	     * (same as $functrace)
602	     */
603	    colonpair = zhalloc(strlen(f->caller) +
604				(f->lineno > 9999 ? 24 : 6));
605#if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD)
606	    sprintf(colonpair, "%s:%lld", f->caller, f->lineno);
607#else
608	    sprintf(colonpair, "%s:%ld", f->caller, (long)f->lineno);
609#endif
610	} else {
611	    /*
612	     * Calling context is a function or eval; we need to find
613	     * the line number in the file where that function was
614	     * defined or the eval was called.  For this we need the
615	     * $funcsourcetrace information for the context above,
616	     * together with the $functrace line number for the current
617	     * context.
618	     */
619	    zlong flineno = f->prev->flineno + f->lineno;
620	    /*
621	     * Line numbers in eval start from 1, not zero,
622	     * so offset by one to get line in file.
623	     */
624	    if (f->prev->tp == FS_EVAL)
625		flineno--;
626	    fname = f->prev->filename ? f->prev->filename : "";
627
628	    colonpair = zhalloc(strlen(fname) + (flineno > 9999 ? 24 : 6));
629#if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD)
630	    sprintf(colonpair, "%s:%lld", fname, flineno);
631#else
632	    sprintf(colonpair, "%s:%ld", fname, (long)flineno);
633#endif
634	}
635
636	*p = colonpair;
637    }
638    *p = NULL;
639
640    return ret;
641}
642
643/* Functions for the builtins special parameter. */
644
645/**/
646static HashNode
647getbuiltin(UNUSED(HashTable ht), const char *name, int dis)
648{
649    Param pm = NULL;
650    Builtin bn;
651
652    pm = (Param) hcalloc(sizeof(struct param));
653    pm->node.nam = dupstring(name);
654    pm->node.flags = PM_SCALAR | PM_READONLY;
655    pm->gsu.s = &nullsetscalar_gsu;
656    if ((bn = (Builtin) builtintab->getnode2(builtintab, name)) &&
657	(dis ? (bn->node.flags & DISABLED) : !(bn->node.flags & DISABLED))) {
658	char *t = ((bn->handlerfunc || (bn->node.flags & BINF_PREFIX)) ?
659		   "defined" : "undefined");
660
661	pm->u.str = dupstring(t);
662    } else {
663	pm->u.str = dupstring("");
664	pm->node.flags |= PM_UNSET;
665    }
666    return &pm->node;
667}
668
669/**/
670static HashNode
671getpmbuiltin(HashTable ht, const char *name)
672{
673    return getbuiltin(ht, name, 0);
674}
675
676/**/
677static HashNode
678getpmdisbuiltin(HashTable ht, const char *name)
679{
680    return getbuiltin(ht, name, DISABLED);
681}
682
683/**/
684static void
685scanbuiltins(UNUSED(HashTable ht), ScanFunc func, int flags, int dis)
686{
687    struct param pm;
688    int i;
689    HashNode hn;
690
691    memset((void *)&pm, 0, sizeof(struct param));
692    pm.node.flags = PM_SCALAR | PM_READONLY;
693    pm.gsu.s = &nullsetscalar_gsu;
694
695    for (i = 0; i < builtintab->hsize; i++)
696	for (hn = builtintab->nodes[i]; hn; hn = hn->next) {
697	    if (dis ? (hn->flags & DISABLED) : !(hn->flags & DISABLED)) {
698		pm.node.nam = hn->nam;
699		if (func != scancountparams &&
700		    ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
701		     !(flags & SCANPM_WANTKEYS))) {
702		    char *t = ((((Builtin) hn)->handlerfunc ||
703				(hn->flags & BINF_PREFIX)) ?
704			       "defined" : "undefined");
705
706		    pm.u.str = dupstring(t);
707		}
708		func(&pm.node, flags);
709	    }
710	}
711}
712
713/**/
714static void
715scanpmbuiltins(HashTable ht, ScanFunc func, int flags)
716{
717    scanbuiltins(ht, func, flags, 0);
718}
719
720/**/
721static void
722scanpmdisbuiltins(HashTable ht, ScanFunc func, int flags)
723{
724    scanbuiltins(ht, func, flags, DISABLED);
725}
726
727/* Functions for the reswords special parameter. */
728
729/**/
730static char **
731getreswords(int dis)
732{
733    int i;
734    HashNode hn;
735    char **ret, **p;
736
737    p = ret = (char **) zhalloc((reswdtab->ct + 1) * sizeof(char *));
738
739    for (i = 0; i < reswdtab->hsize; i++)
740	for (hn = reswdtab->nodes[i]; hn; hn = hn->next)
741	    if (dis ? (hn->flags & DISABLED) : !(hn->flags & DISABLED))
742		*p++ = dupstring(hn->nam);
743    *p = NULL;
744
745    return ret;
746}
747
748/**/
749static char **
750reswordsgetfn(UNUSED(Param pm))
751{
752    return getreswords(0);
753}
754
755/**/
756static char **
757disreswordsgetfn(UNUSED(Param pm))
758{
759    return getreswords(DISABLED);
760}
761
762/* Functions for the patchars special parameter. */
763
764/**/
765static char **
766getpatchars(int dis)
767{
768    int i;
769    char **ret, **p;
770
771    p = ret = (char **) zhalloc(ZPC_COUNT * sizeof(char *));
772
773    for (i = 0; i < ZPC_COUNT; i++)
774	if (zpc_strings[i] && !dis == !zpc_disables[i])
775	    *p++ = dupstring(zpc_strings[i]);
776
777    *p = NULL;
778
779    return ret;
780}
781
782static char **
783patcharsgetfn(UNUSED(Param pm))
784{
785    return getpatchars(0);
786}
787
788static char **
789dispatcharsgetfn(UNUSED(Param pm))
790{
791    return getpatchars(1);
792}
793
794/* Functions for the options special parameter. */
795
796/**/
797static void
798setpmoption(Param pm, char *value)
799{
800    int n;
801
802    if (!value || (strcmp(value, "on") && strcmp(value, "off")))
803	zwarn("invalid value: %s", value);
804    else if (!(n = optlookup(pm->node.nam)))
805	zwarn("no such option: %s", pm->node.nam);
806    else if (dosetopt(n, (value && strcmp(value, "off")), 0, opts))
807	zwarn("can't change option: %s", pm->node.nam);
808    zsfree(value);
809}
810
811/**/
812static void
813unsetpmoption(Param pm, UNUSED(int exp))
814{
815    int n;
816
817    if (!(n = optlookup(pm->node.nam)))
818	zwarn("no such option: %s", pm->node.nam);
819    else if (dosetopt(n, 0, 0, opts))
820	zwarn("can't change option: %s", pm->node.nam);
821}
822
823/**/
824static void
825setpmoptions(UNUSED(Param pm), HashTable ht)
826{
827    int i;
828    HashNode hn;
829
830    if (!ht)
831	return;
832
833    for (i = 0; i < ht->hsize; i++)
834	for (hn = ht->nodes[i]; hn; hn = hn->next) {
835	    struct value v;
836	    char *val;
837
838	    v.isarr = v.flags = v.start = 0;
839	    v.end = -1;
840	    v.arr = NULL;
841	    v.pm = (Param) hn;
842
843	    val = getstrvalue(&v);
844	    if (!val || (strcmp(val, "on") && strcmp(val, "off")))
845		zwarn("invalid value: %s", val);
846	    else if (dosetopt(optlookup(hn->nam),
847			      (val && strcmp(val, "off")), 0, opts))
848		zwarn("can't change option: %s", hn->nam);
849	}
850    deleteparamtable(ht);
851}
852
853static const struct gsu_scalar pmoption_gsu =
854{ strgetfn, setpmoption, unsetpmoption };
855
856/**/
857static HashNode
858getpmoption(UNUSED(HashTable ht), const char *name)
859{
860    Param pm = NULL;
861    int n;
862
863    pm = (Param) hcalloc(sizeof(struct param));
864    pm->node.nam = dupstring(name);
865    pm->node.flags = PM_SCALAR;
866    pm->gsu.s = &pmoption_gsu;
867
868    if ((n = optlookup(name)))
869    {
870	int ison;
871	if (n > 0)
872	    ison = opts[n];
873	else
874	    ison = !opts[-n];
875	pm->u.str = dupstring(ison ? "on" : "off");
876    }
877    else {
878	pm->u.str = dupstring("");
879	pm->node.flags |= PM_UNSET;
880    }
881    return &pm->node;
882}
883
884/**/
885static void
886scanpmoptions(UNUSED(HashTable ht), ScanFunc func, int flags)
887{
888    struct param pm;
889    int i;
890    HashNode hn;
891
892    memset((void *)&pm, 0, sizeof(struct param));
893    pm.node.flags = PM_SCALAR;
894    pm.gsu.s = &pmoption_gsu;
895
896    for (i = 0; i < optiontab->hsize; i++)
897	for (hn = optiontab->nodes[i]; hn; hn = hn->next) {
898	    int optno = ((Optname) hn)->optno, ison;
899	    pm.node.nam = hn->nam;
900	    ison = optno < 0 ? !opts[-optno] : opts[optno];
901	    pm.u.str = dupstring(ison ? "on" : "off");
902	    func(&pm.node, flags);
903	}
904}
905
906/* Functions for the modules special parameter. */
907
908/**/
909static HashNode
910getpmmodule(UNUSED(HashTable ht), const char *name)
911{
912    Param pm = NULL;
913    char *type = NULL;
914    Module m;
915
916    pm = (Param) hcalloc(sizeof(struct param));
917    pm->node.nam = dupstring(name);
918    pm->node.flags = PM_SCALAR | PM_READONLY;
919    pm->gsu.s = &nullsetscalar_gsu;
920
921    m = (Module)modulestab->getnode2(modulestab, name);
922
923    if (!m)
924	return NULL;
925    if (m->u.handle && !(m->node.flags & MOD_UNLOAD)) {
926	type = ((m->node.flags & MOD_ALIAS) ?
927		dyncat("alias:", m->u.alias) : "loaded");
928    }
929    if (!type) {
930	if (m->autoloads && firstnode(m->autoloads))
931	    type = "autoloaded";
932    }
933    if (type)
934	pm->u.str = dupstring(type);
935    else {
936	pm->u.str = dupstring("");
937	pm->node.flags |= PM_UNSET;
938    }
939    return &pm->node;
940}
941
942/**/
943static void
944scanpmmodules(UNUSED(HashTable ht), ScanFunc func, int flags)
945{
946    struct param pm;
947    int i;
948    HashNode hn;
949    LinkList done = newlinklist();
950    Module m;
951    Conddef p;
952    char *loaded = dupstring("loaded");
953
954    memset((void *)&pm, 0, sizeof(struct param));
955    pm.node.flags = PM_SCALAR | PM_READONLY;
956    pm.gsu.s = &nullsetscalar_gsu;
957
958    for (i = 0; i < modulestab->hsize; i++) {
959	for (hn = modulestab->nodes[i]; hn; hn = hn->next) {
960	    m = (Module) hn;
961	    if (m->u.handle && !(m->node.flags & MOD_UNLOAD)) {
962		pm.node.nam = m->node.nam;
963		pm.u.str = ((m->node.flags & MOD_ALIAS) ?
964			    dyncat("alias:", m->u.alias) : loaded);
965		addlinknode(done, pm.node.nam);
966		func(&pm.node, flags);
967	    }
968	}
969    }
970    pm.u.str = dupstring("autoloaded");
971    for (i = 0; i < builtintab->hsize; i++)
972	for (hn = builtintab->nodes[i]; hn; hn = hn->next) {
973	    if (!(((Builtin) hn)->node.flags & BINF_ADDED) &&
974		!linknodebystring(done, ((Builtin) hn)->optstr)) {
975		pm.node.nam = ((Builtin) hn)->optstr;
976		addlinknode(done, pm.node.nam);
977		func(&pm.node, flags);
978	    }
979	}
980    for (p = condtab; p; p = p->next)
981	if (p->module && !linknodebystring(done, p->module)) {
982	    pm.node.nam = p->module;
983	    addlinknode(done, pm.node.nam);
984	    func(&pm.node, flags);
985	}
986    for (i = 0; i < realparamtab->hsize; i++)
987	for (hn = realparamtab->nodes[i]; hn; hn = hn->next) {
988	    if ((((Param) hn)->node.flags & PM_AUTOLOAD) &&
989		!linknodebystring(done, ((Param) hn)->u.str)) {
990		pm.node.nam = ((Param) hn)->u.str;
991		addlinknode(done, pm.node.nam);
992		func(&pm.node, flags);
993	    }
994	}
995}
996
997/* Functions for the dirstack special parameter. */
998
999/**/
1000static void
1001dirssetfn(UNUSED(Param pm), char **x)
1002{
1003    char **ox = x;
1004
1005    if (!incleanup) {
1006	freelinklist(dirstack, freestr);
1007	dirstack = znewlinklist();
1008	while (x && *x)
1009	    zaddlinknode(dirstack, ztrdup(*x++));
1010    }
1011    if (ox)
1012	freearray(ox);
1013}
1014
1015/**/
1016static char **
1017dirsgetfn(UNUSED(Param pm))
1018{
1019    return hlinklist2array(dirstack, 1);
1020}
1021
1022/* Functions for the history special parameter. */
1023
1024/**/
1025static HashNode
1026getpmhistory(UNUSED(HashTable ht), const char *name)
1027{
1028    Param pm = NULL;
1029    Histent he;
1030    const char *p;
1031    int ok = 1;
1032
1033    pm = (Param) hcalloc(sizeof(struct param));
1034    pm->node.nam = dupstring(name);
1035    pm->node.flags = PM_SCALAR | PM_READONLY;
1036    pm->gsu.s = &nullsetscalar_gsu;
1037
1038    if (*name != '0' || name[1]) {
1039	if (*name == '0')
1040	    ok = 0;
1041	else {
1042	    for (p = name; *p && idigit(*p); p++);
1043	    if (*p)
1044		ok = 0;
1045	}
1046    }
1047    if (ok && (he = quietgethist(atoi(name))))
1048	pm->u.str = dupstring(he->node.nam);
1049    else {
1050	pm->u.str = dupstring("");
1051	pm->node.flags |= PM_UNSET;
1052    }
1053    return &pm->node;
1054}
1055
1056/**/
1057static void
1058scanpmhistory(UNUSED(HashTable ht), ScanFunc func, int flags)
1059{
1060    struct param pm;
1061    int i = addhistnum(curhist, -1, HIST_FOREIGN);
1062    Histent he = gethistent(i, GETHIST_UPWARD);
1063    char buf[40];
1064
1065    memset((void *)&pm, 0, sizeof(struct param));
1066    pm.node.flags = PM_SCALAR | PM_READONLY;
1067    pm.gsu.s = &nullsetscalar_gsu;
1068
1069    while (he) {
1070	if (func != scancountparams) {
1071	    convbase(buf, he->histnum, 10);
1072	    pm.node.nam = dupstring(buf);
1073	    if ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
1074		!(flags & SCANPM_WANTKEYS))
1075		pm.u.str = dupstring(he->node.nam);
1076	}
1077	func(&pm.node, flags);
1078
1079	he = up_histent(he);
1080    }
1081}
1082
1083/* Function for the historywords special parameter. */
1084
1085/**/
1086static char **
1087histwgetfn(UNUSED(Param pm))
1088{
1089    char *h, *e, sav;
1090    LinkList l = newlinklist(), ll;
1091    LinkNode n;
1092    int i = addhistnum(curhist, -1, HIST_FOREIGN), iw;
1093    Histent he = gethistent(i, GETHIST_UPWARD);
1094
1095    if ((ll = bufferwords(NULL, NULL, NULL, 0)))
1096        for (n = firstnode(ll); n; incnode(n))
1097            pushnode(l, getdata(n));
1098
1099    while (he) {
1100	for (iw = he->nwords - 1; iw >= 0; iw--) {
1101	    h = he->node.nam + he->words[iw * 2];
1102	    e = he->node.nam + he->words[iw * 2 + 1];
1103	    sav = *e;
1104	    *e = '\0';
1105	    addlinknode(l, dupstring(h));
1106	    *e = sav;
1107	}
1108	he = up_histent(he);
1109    }
1110
1111    return hlinklist2array(l, 0);
1112}
1113
1114/* Functions for the jobtexts special parameter. */
1115
1116/**/
1117static char *
1118pmjobtext(int job)
1119{
1120    Process pn;
1121    int len = 1;
1122    char *ret;
1123
1124    for (pn = jobtab[job].procs; pn; pn = pn->next)
1125	len += strlen(pn->text) + 3;
1126
1127    ret = (char *) zhalloc(len);
1128    ret[0] = '\0';
1129
1130    for (pn = jobtab[job].procs; pn; pn = pn->next) {
1131	strcat(ret, pn->text);
1132	if (pn->next)
1133	    strcat(ret, " | ");
1134    }
1135    return ret;
1136}
1137
1138/**/
1139static HashNode
1140getpmjobtext(UNUSED(HashTable ht), const char *name)
1141{
1142    Param pm = NULL;
1143    int job;
1144    char *pend;
1145
1146    pm = (Param) hcalloc(sizeof(struct param));
1147    pm->node.nam = dupstring(name);
1148    pm->node.flags = PM_SCALAR | PM_READONLY;
1149    pm->gsu.s = &nullsetscalar_gsu;
1150
1151    job = strtod(name, &pend);
1152    /* Non-numeric keys are looked up by job name */
1153    if (*pend)
1154	job = getjob(name, NULL);
1155    if (job >= 1 && job <= maxjob &&
1156	jobtab[job].stat && jobtab[job].procs &&
1157	!(jobtab[job].stat & STAT_NOPRINT))
1158	pm->u.str = pmjobtext(job);
1159    else {
1160	pm->u.str = dupstring("");
1161	pm->node.flags |= PM_UNSET;
1162    }
1163    return &pm->node;
1164}
1165
1166/**/
1167static void
1168scanpmjobtexts(UNUSED(HashTable ht), ScanFunc func, int flags)
1169{
1170    struct param pm;
1171    int job;
1172    char buf[40];
1173
1174    memset((void *)&pm, 0, sizeof(struct param));
1175    pm.node.flags = PM_SCALAR | PM_READONLY;
1176    pm.gsu.s = &nullsetscalar_gsu;
1177
1178    for (job = 1; job <= maxjob; job++) {
1179	if (jobtab[job].stat && jobtab[job].procs &&
1180	    !(jobtab[job].stat & STAT_NOPRINT)) {
1181	    if (func != scancountparams) {
1182		sprintf(buf, "%d", job);
1183		pm.node.nam = dupstring(buf);
1184		if ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
1185		    !(flags & SCANPM_WANTKEYS))
1186		    pm.u.str = pmjobtext(job);
1187	    }
1188	    func(&pm.node, flags);
1189	}
1190    }
1191}
1192
1193/* Functions for the jobstates special parameter. */
1194
1195/**/
1196static char *
1197pmjobstate(int job)
1198{
1199    Process pn;
1200    char buf[256], buf2[128], *ret, *state, *cp;
1201
1202    if (job == curjob)
1203	cp = ":+";
1204    else if (job == prevjob)
1205	cp = ":-";
1206    else
1207	cp = ":";
1208
1209    if (jobtab[job].stat & STAT_DONE)
1210	ret = dyncat("done", cp);
1211    else if (jobtab[job].stat & STAT_STOPPED)
1212	ret = dyncat("suspended", cp);
1213    else
1214	ret = dyncat("running", cp);
1215
1216    for (pn = jobtab[job].procs; pn; pn = pn->next) {
1217
1218	if (pn->status == SP_RUNNING)
1219	    state = "running";
1220	else if (WIFEXITED(pn->status)) {
1221	    if (WEXITSTATUS(pn->status))
1222		sprintf((state = buf2), "exit %d", (pn->status));
1223	    else
1224		state = "done";
1225	} else if (WIFSTOPPED(pn->status))
1226	    state = sigmsg(WSTOPSIG(pn->status));
1227	else if (WCOREDUMP(pn->status))
1228	    sprintf((state = buf2), "%s (core dumped)",
1229		    sigmsg(WTERMSIG(pn->status)));
1230	else
1231	    state = sigmsg(WTERMSIG(pn->status));
1232
1233	sprintf(buf, ":%d=%s", (int)pn->pid, state);
1234
1235	ret = dyncat(ret, buf);
1236    }
1237    return ret;
1238}
1239
1240/**/
1241static HashNode
1242getpmjobstate(UNUSED(HashTable ht), const char *name)
1243{
1244    Param pm = NULL;
1245    int job;
1246    char *pend;
1247
1248    pm = (Param) hcalloc(sizeof(struct param));
1249    pm->node.nam = dupstring(name);
1250    pm->node.flags = PM_SCALAR | PM_READONLY;
1251    pm->gsu.s = &nullsetscalar_gsu;
1252
1253    job = strtod(name, &pend);
1254    if (*pend)
1255	job = getjob(name, NULL);
1256    if (job >= 1 && job <= maxjob &&
1257	jobtab[job].stat && jobtab[job].procs &&
1258	!(jobtab[job].stat & STAT_NOPRINT))
1259	pm->u.str = pmjobstate(job);
1260    else {
1261	pm->u.str = dupstring("");
1262	pm->node.flags |= PM_UNSET;
1263    }
1264    return &pm->node;
1265}
1266
1267/**/
1268static void
1269scanpmjobstates(UNUSED(HashTable ht), ScanFunc func, int flags)
1270{
1271    struct param pm;
1272    int job;
1273    char buf[40];
1274
1275    memset((void *)&pm, 0, sizeof(struct param));
1276    pm.node.flags = PM_SCALAR | PM_READONLY;
1277    pm.gsu.s = &nullsetscalar_gsu;
1278
1279    for (job = 1; job <= maxjob; job++) {
1280	if (jobtab[job].stat && jobtab[job].procs &&
1281	    !(jobtab[job].stat & STAT_NOPRINT)) {
1282	    if (func != scancountparams) {
1283		sprintf(buf, "%d", job);
1284		pm.node.nam = dupstring(buf);
1285		if ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
1286		    !(flags & SCANPM_WANTKEYS))
1287		    pm.u.str = pmjobstate(job);
1288	    }
1289	    func(&pm.node, flags);
1290	}
1291    }
1292}
1293
1294/* Functions for the jobdirs special parameter. */
1295
1296/**/
1297static char *
1298pmjobdir(int job)
1299{
1300    char *ret;
1301
1302    ret = dupstring(jobtab[job].pwd ? jobtab[job].pwd : pwd);
1303    return ret;
1304}
1305
1306/**/
1307static HashNode
1308getpmjobdir(UNUSED(HashTable ht), const char *name)
1309{
1310    Param pm = NULL;
1311    int job;
1312    char *pend;
1313
1314    pm = (Param) hcalloc(sizeof(struct param));
1315    pm->node.nam = dupstring(name);
1316    pm->node.flags = PM_SCALAR | PM_READONLY;
1317    pm->gsu.s = &nullsetscalar_gsu;
1318
1319    job = strtod(name, &pend);
1320    if (*pend)
1321	job = getjob(name, NULL);
1322    if (job >= 1 && job <= maxjob &&
1323	jobtab[job].stat && jobtab[job].procs &&
1324	!(jobtab[job].stat & STAT_NOPRINT))
1325	pm->u.str = pmjobdir(job);
1326    else {
1327	pm->u.str = dupstring("");
1328	pm->node.flags |= PM_UNSET;
1329    }
1330    return &pm->node;
1331}
1332
1333/**/
1334static void
1335scanpmjobdirs(UNUSED(HashTable ht), ScanFunc func, int flags)
1336{
1337    struct param pm;
1338    int job;
1339    char buf[40];
1340
1341    memset((void *)&pm, 0, sizeof(struct param));
1342    pm.node.flags = PM_SCALAR | PM_READONLY;
1343    pm.gsu.s = &nullsetscalar_gsu;
1344
1345    for (job = 1; job <= maxjob; job++) {
1346       if (jobtab[job].stat && jobtab[job].procs &&
1347           !(jobtab[job].stat & STAT_NOPRINT)) {
1348           if (func != scancountparams) {
1349	       sprintf(buf, "%d", job);
1350	       pm.node.nam = dupstring(buf);
1351               if ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
1352		   !(flags & SCANPM_WANTKEYS))
1353		   pm.u.str = pmjobdir(job);
1354	   }
1355           func(&pm.node, flags);
1356       }
1357    }
1358}
1359
1360/* Functions for the nameddirs special parameter. */
1361
1362/**/
1363static void
1364setpmnameddir(Param pm, char *value)
1365{
1366    if (!value)
1367	zwarn("invalid value: ''");
1368    else {
1369	Nameddir nd = (Nameddir) zshcalloc(sizeof(*nd));
1370
1371	nd->node.flags = 0;
1372	nd->dir = value;
1373	nameddirtab->addnode(nameddirtab, ztrdup(pm->node.nam), nd);
1374    }
1375}
1376
1377/**/
1378static void
1379unsetpmnameddir(Param pm, UNUSED(int exp))
1380{
1381    HashNode hd = nameddirtab->removenode(nameddirtab, pm->node.nam);
1382
1383    if (hd)
1384	nameddirtab->freenode(hd);
1385}
1386
1387/**/
1388static void
1389setpmnameddirs(UNUSED(Param pm), HashTable ht)
1390{
1391    int i;
1392    HashNode hn, next, hd;
1393
1394    if (!ht)
1395	return;
1396
1397    for (i = 0; i < nameddirtab->hsize; i++)
1398	for (hn = nameddirtab->nodes[i]; hn; hn = next) {
1399	    next = hn->next;
1400	    if (!(((Nameddir) hn)->node.flags & ND_USERNAME) &&
1401		(hd = nameddirtab->removenode(nameddirtab, hn->nam)))
1402		nameddirtab->freenode(hd);
1403	}
1404
1405    for (i = 0; i < ht->hsize; i++)
1406	for (hn = ht->nodes[i]; hn; hn = hn->next) {
1407	    struct value v;
1408	    char *val;
1409
1410	    v.isarr = v.flags = v.start = 0;
1411	    v.end = -1;
1412	    v.arr = NULL;
1413	    v.pm = (Param) hn;
1414
1415	    if (!(val = getstrvalue(&v)))
1416		zwarn("invalid value: ''");
1417	    else {
1418		Nameddir nd = (Nameddir) zshcalloc(sizeof(*nd));
1419
1420		nd->node.flags = 0;
1421		nd->dir = ztrdup(val);
1422		nameddirtab->addnode(nameddirtab, ztrdup(hn->nam), nd);
1423	    }
1424	}
1425
1426    /* The INTERACTIVE stuff ensures that the dirs are not immediatly removed
1427     * when the sub-pms are deleted. */
1428
1429    i = opts[INTERACTIVE];
1430    opts[INTERACTIVE] = 0;
1431    deleteparamtable(ht);
1432    opts[INTERACTIVE] = i;
1433}
1434
1435static const struct gsu_scalar pmnamedir_gsu =
1436{ strgetfn, setpmnameddir, unsetpmnameddir };
1437
1438/**/
1439static HashNode
1440getpmnameddir(UNUSED(HashTable ht), const char *name)
1441{
1442    Param pm = NULL;
1443    Nameddir nd;
1444
1445    pm = (Param) hcalloc(sizeof(struct param));
1446    pm->node.nam = dupstring(name);
1447    pm->node.flags = PM_SCALAR;
1448    pm->gsu.s = &pmnamedir_gsu;
1449    if ((nd = (Nameddir) nameddirtab->getnode(nameddirtab, name)) &&
1450	!(nd->node.flags & ND_USERNAME))
1451	pm->u.str = dupstring(nd->dir);
1452    else {
1453	pm->u.str = dupstring("");
1454	pm->node.flags |= PM_UNSET;
1455    }
1456    return &pm->node;
1457}
1458
1459/**/
1460static void
1461scanpmnameddirs(UNUSED(HashTable ht), ScanFunc func, int flags)
1462{
1463    struct param pm;
1464    int i;
1465    HashNode hn;
1466    Nameddir nd;
1467
1468    memset((void *)&pm, 0, sizeof(struct param));
1469    pm.node.flags = PM_SCALAR;
1470    pm.gsu.s = &pmnamedir_gsu;
1471
1472    for (i = 0; i < nameddirtab->hsize; i++)
1473	for (hn = nameddirtab->nodes[i]; hn; hn = hn->next) {
1474	    if (!((nd = (Nameddir) hn)->node.flags & ND_USERNAME)) {
1475		pm.node.nam = hn->nam;
1476		if (func != scancountparams &&
1477		    ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
1478		     !(flags & SCANPM_WANTKEYS)))
1479		    pm.u.str = dupstring(nd->dir);
1480		func(&pm.node, flags);
1481	    }
1482	}
1483}
1484
1485/* Functions for the userdirs special parameter. */
1486
1487/**/
1488static HashNode
1489getpmuserdir(UNUSED(HashTable ht), const char *name)
1490{
1491    Param pm = NULL;
1492    Nameddir nd;
1493
1494    nameddirtab->filltable(nameddirtab);
1495
1496    pm = (Param) hcalloc(sizeof(struct param));
1497    pm->node.nam = dupstring(name);
1498    pm->node.flags = PM_SCALAR | PM_READONLY;
1499    pm->gsu.s = &nullsetscalar_gsu;
1500    if ((nd = (Nameddir) nameddirtab->getnode(nameddirtab, name)) &&
1501	(nd->node.flags & ND_USERNAME))
1502	pm->u.str = dupstring(nd->dir);
1503    else {
1504	pm->u.str = dupstring("");
1505	pm->node.flags |= PM_UNSET;
1506    }
1507    return &pm->node;
1508}
1509
1510/**/
1511static void
1512scanpmuserdirs(UNUSED(HashTable ht), ScanFunc func, int flags)
1513{
1514    struct param pm;
1515    int i;
1516    HashNode hn;
1517    Nameddir nd;
1518
1519    nameddirtab->filltable(nameddirtab);
1520
1521    memset((void *)&pm, 0, sizeof(struct param));
1522    pm.node.flags = PM_SCALAR | PM_READONLY;
1523    pm.gsu.s = &nullsetscalar_gsu;
1524
1525    for (i = 0; i < nameddirtab->hsize; i++)
1526	for (hn = nameddirtab->nodes[i]; hn; hn = hn->next) {
1527	    if ((nd = (Nameddir) hn)->node.flags & ND_USERNAME) {
1528		pm.node.nam = hn->nam;
1529		if (func != scancountparams &&
1530		    ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
1531		     !(flags & SCANPM_WANTKEYS)))
1532		    pm.u.str = dupstring(nd->dir);
1533		func(&pm.node, flags);
1534	    }
1535	}
1536}
1537
1538/* Functions for the raliases, galiases and saliases special parameters. */
1539
1540/**/
1541static void
1542setalias(HashTable ht, Param pm, char *value, int flags)
1543{
1544    ht->addnode(ht, ztrdup(pm->node.nam),
1545		createaliasnode(value, flags));
1546}
1547
1548/**/
1549static void
1550setpmralias(Param pm, char *value)
1551{
1552    setalias(aliastab, pm, value, 0);
1553}
1554
1555/**/
1556static void
1557setpmdisralias(Param pm, char *value)
1558{
1559    setalias(aliastab, pm, value, DISABLED);
1560}
1561
1562/**/
1563static void
1564setpmgalias(Param pm, char *value)
1565{
1566    setalias(aliastab, pm, value, ALIAS_GLOBAL);
1567}
1568
1569/**/
1570static void
1571setpmdisgalias(Param pm, char *value)
1572{
1573    setalias(aliastab, pm, value, ALIAS_GLOBAL|DISABLED);
1574}
1575
1576/**/
1577static void
1578setpmsalias(Param pm, char *value)
1579{
1580    setalias(sufaliastab, pm, value, ALIAS_SUFFIX);
1581}
1582
1583/**/
1584static void
1585setpmdissalias(Param pm, char *value)
1586{
1587    setalias(sufaliastab, pm, value, ALIAS_SUFFIX|DISABLED);
1588}
1589
1590/**/
1591static void
1592unsetpmalias(Param pm, UNUSED(int exp))
1593{
1594    HashNode hd = aliastab->removenode(aliastab, pm->node.nam);
1595
1596    if (hd)
1597	aliastab->freenode(hd);
1598}
1599
1600/**/
1601static void
1602unsetpmsalias(Param pm, UNUSED(int exp))
1603{
1604    HashNode hd = sufaliastab->removenode(sufaliastab, pm->node.nam);
1605
1606    if (hd)
1607	sufaliastab->freenode(hd);
1608}
1609
1610/**/
1611static void
1612setaliases(HashTable alht, UNUSED(Param pm), HashTable ht, int flags)
1613{
1614    int i;
1615    HashNode hn, next, hd;
1616
1617    if (!ht)
1618	return;
1619
1620    for (i = 0; i < alht->hsize; i++)
1621	for (hn = alht->nodes[i]; hn; hn = next) {
1622	    next = hn->next;
1623	    /*
1624	     * The following respects the DISABLED flag, e.g.
1625	     * we get a different behaviour for raliases and dis_raliases.
1626	     * The predecessor to this code didn't do that; presumably
1627	     * that was a bug.
1628	     */
1629	    if (flags == ((Alias)hn)->node.flags &&
1630		(hd = alht->removenode(alht, hn->nam)))
1631		alht->freenode(hd);
1632	}
1633
1634    for (i = 0; i < ht->hsize; i++)
1635	for (hn = ht->nodes[i]; hn; hn = hn->next) {
1636	    struct value v;
1637	    char *val;
1638
1639	    v.isarr = v.flags = v.start = 0;
1640	    v.end = -1;
1641	    v.arr = NULL;
1642	    v.pm = (Param) hn;
1643
1644	    if ((val = getstrvalue(&v)))
1645		alht->addnode(alht, ztrdup(hn->nam),
1646			      createaliasnode(ztrdup(val), flags));
1647	}
1648    deleteparamtable(ht);
1649}
1650
1651/**/
1652static void
1653setpmraliases(Param pm, HashTable ht)
1654{
1655    setaliases(aliastab, pm, ht, 0);
1656}
1657
1658/**/
1659static void
1660setpmdisraliases(Param pm, HashTable ht)
1661{
1662    setaliases(aliastab, pm, ht, DISABLED);
1663}
1664
1665/**/
1666static void
1667setpmgaliases(Param pm, HashTable ht)
1668{
1669    setaliases(aliastab, pm, ht, ALIAS_GLOBAL);
1670}
1671
1672/**/
1673static void
1674setpmdisgaliases(Param pm, HashTable ht)
1675{
1676    setaliases(aliastab, pm, ht, ALIAS_GLOBAL|DISABLED);
1677}
1678
1679/**/
1680static void
1681setpmsaliases(Param pm, HashTable ht)
1682{
1683    setaliases(sufaliastab, pm, ht, ALIAS_SUFFIX);
1684}
1685
1686/**/
1687static void
1688setpmdissaliases(Param pm, HashTable ht)
1689{
1690    setaliases(sufaliastab, pm, ht, ALIAS_SUFFIX|DISABLED);
1691}
1692
1693static const struct gsu_scalar pmralias_gsu =
1694{ strgetfn, setpmralias, unsetpmalias };
1695static const struct gsu_scalar pmgalias_gsu =
1696{ strgetfn, setpmgalias, unsetpmalias };
1697static const struct gsu_scalar pmsalias_gsu =
1698{ strgetfn, setpmsalias, unsetpmsalias };
1699static const struct gsu_scalar pmdisralias_gsu =
1700{ strgetfn, setpmdisralias, unsetpmalias };
1701static const struct gsu_scalar pmdisgalias_gsu =
1702{ strgetfn, setpmdisgalias, unsetpmalias };
1703static const struct gsu_scalar pmdissalias_gsu =
1704{ strgetfn, setpmdissalias, unsetpmsalias };
1705
1706/**/
1707static void
1708assignaliasdefs(Param pm, int flags)
1709{
1710    pm->node.flags = PM_SCALAR;
1711
1712    /* we really need to squirrel the flags away somewhere... */
1713    switch (flags) {
1714    case 0:
1715	pm->gsu.s = &pmralias_gsu;
1716	break;
1717
1718    case ALIAS_GLOBAL:
1719	pm->gsu.s = &pmgalias_gsu;
1720	break;
1721
1722    case ALIAS_SUFFIX:
1723	pm->gsu.s = &pmsalias_gsu;
1724	break;
1725
1726    case DISABLED:
1727	pm->gsu.s = &pmdisralias_gsu;
1728	break;
1729
1730    case ALIAS_GLOBAL|DISABLED:
1731	pm->gsu.s = &pmdisgalias_gsu;
1732	break;
1733
1734    case ALIAS_SUFFIX|DISABLED:
1735	pm->gsu.s = &pmdissalias_gsu;
1736	break;
1737    }
1738}
1739
1740/**/
1741static HashNode
1742getalias(HashTable alht, UNUSED(HashTable ht), const char *name, int flags)
1743{
1744    Param pm = NULL;
1745    Alias al;
1746
1747    pm = (Param) hcalloc(sizeof(struct param));
1748    pm->node.nam = dupstring(name);
1749
1750    assignaliasdefs(pm, flags);
1751
1752    if ((al = (Alias) alht->getnode2(alht, name)) &&
1753	flags == al->node.flags)
1754	pm->u.str = dupstring(al->text);
1755    else {
1756	pm->u.str = dupstring("");
1757	pm->node.flags |= PM_UNSET;
1758    }
1759    return &pm->node;
1760}
1761
1762/**/
1763static HashNode
1764getpmralias(HashTable ht, const char *name)
1765{
1766    return getalias(aliastab, ht, name, 0);
1767}
1768
1769/**/
1770static HashNode
1771getpmdisralias(HashTable ht, const char *name)
1772{
1773    return getalias(aliastab, ht, name, DISABLED);
1774}
1775
1776/**/
1777static HashNode
1778getpmgalias(HashTable ht, const char *name)
1779{
1780    return getalias(aliastab, ht, name, ALIAS_GLOBAL);
1781}
1782
1783/**/
1784static HashNode
1785getpmdisgalias(HashTable ht, const char *name)
1786{
1787    return getalias(aliastab, ht, name, ALIAS_GLOBAL|DISABLED);
1788}
1789
1790/**/
1791static HashNode
1792getpmsalias(HashTable ht, const char *name)
1793{
1794    return getalias(sufaliastab, ht, name, ALIAS_SUFFIX);
1795}
1796
1797/**/
1798static HashNode
1799getpmdissalias(HashTable ht, const char *name)
1800{
1801    return getalias(sufaliastab, ht, name, ALIAS_SUFFIX|DISABLED);
1802}
1803
1804/**/
1805static void
1806scanaliases(HashTable alht, UNUSED(HashTable ht), ScanFunc func,
1807	    int pmflags, int alflags)
1808{
1809    struct param pm;
1810    int i;
1811    Alias al;
1812
1813    memset((void *)&pm, 0, sizeof(struct param));
1814    assignaliasdefs(&pm, alflags);
1815
1816    for (i = 0; i < alht->hsize; i++)
1817	for (al = (Alias) alht->nodes[i]; al; al = (Alias) al->node.next) {
1818	    if (alflags == al->node.flags) {
1819		pm.node.nam = al->node.nam;
1820		if (func != scancountparams &&
1821		    ((pmflags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
1822		     !(pmflags & SCANPM_WANTKEYS)))
1823		    pm.u.str = dupstring(al->text);
1824		func(&pm.node, pmflags);
1825	    }
1826	}
1827}
1828
1829/**/
1830static void
1831scanpmraliases(HashTable ht, ScanFunc func, int flags)
1832{
1833    scanaliases(aliastab, ht, func, flags, 0);
1834}
1835
1836/**/
1837static void
1838scanpmdisraliases(HashTable ht, ScanFunc func, int flags)
1839{
1840    scanaliases(aliastab, ht, func, flags, DISABLED);
1841}
1842
1843/**/
1844static void
1845scanpmgaliases(HashTable ht, ScanFunc func, int flags)
1846{
1847    scanaliases(aliastab, ht, func, flags, ALIAS_GLOBAL);
1848}
1849
1850/**/
1851static void
1852scanpmdisgaliases(HashTable ht, ScanFunc func, int flags)
1853{
1854    scanaliases(aliastab, ht, func, flags, ALIAS_GLOBAL|DISABLED);
1855}
1856
1857/**/
1858static void
1859scanpmsaliases(HashTable ht, ScanFunc func, int flags)
1860{
1861    scanaliases(sufaliastab, ht, func, flags, ALIAS_SUFFIX);
1862}
1863
1864/**/
1865static void
1866scanpmdissaliases(HashTable ht, ScanFunc func, int flags)
1867{
1868    scanaliases(sufaliastab, ht, func, flags, ALIAS_SUFFIX|DISABLED);
1869}
1870
1871
1872/* Functions for the usergroups special parameter */
1873
1874/*
1875 * Get GID and names for groups of which the current user is a member.
1876 */
1877
1878/**/
1879static Groupset get_all_groups(void)
1880{
1881    Groupset gs = zhalloc(sizeof(*gs));
1882    Groupmap gaptr;
1883    gid_t *list, *lptr, egid;
1884    int add_egid;
1885    struct group *grptr;
1886
1887    egid = getegid();
1888    add_egid = 1;
1889    gs->num = getgroups(0, NULL);
1890    if (gs->num > 0) {
1891	list = zhalloc(gs->num * sizeof(*list));
1892	if (getgroups(gs->num, list) < 0) {
1893	    return NULL;
1894	}
1895
1896	/*
1897	 * It's unspecified whether $EGID is included in the
1898	 * group set, so check.
1899	 */
1900	for (lptr = list; lptr < list + gs->num; lptr++) {
1901	    if (*lptr == egid) {
1902		add_egid = 0;
1903		break;
1904	    }
1905	}
1906	gs->array = zhalloc((gs->num + add_egid) * sizeof(*gs->array));
1907	/* Put EGID if needed first */
1908	gaptr = gs->array + add_egid;
1909	for (lptr = list; lptr < list + gs->num; lptr++) {
1910	    gaptr->gid = *lptr;
1911	    gaptr++;
1912	}
1913	gs->num += add_egid;
1914    } else {
1915	/* Just use effective GID */
1916	gs->num = 1;
1917	gs->array = zhalloc(sizeof(*gs->array));
1918    }
1919    if (add_egid) {
1920	gs->array->gid = egid;
1921    }
1922
1923    /* Get group names */
1924    for (gaptr = gs->array; gaptr < gs->array + gs->num; gaptr++) {
1925	grptr = getgrgid(gaptr->gid);
1926	if (!grptr) {
1927	    return NULL;
1928	}
1929	gaptr->name = dupstring(grptr->gr_name);
1930    }
1931
1932    return gs;
1933}
1934
1935/* Standard hash element lookup. */
1936
1937/**/
1938static HashNode
1939getpmusergroups(UNUSED(HashTable ht), const char *name)
1940{
1941    Param pm = NULL;
1942    Groupset gs = get_all_groups();
1943    Groupmap gaptr;
1944
1945    pm = (Param)hcalloc(sizeof(struct param));
1946    pm->node.nam = dupstring(name);
1947    pm->node.flags = PM_SCALAR | PM_READONLY;
1948    pm->gsu.s = &nullsetscalar_gsu;
1949
1950    if (!gs) {
1951	zerr("failed to retrieve groups for user: %e", errno);
1952	pm->u.str = dupstring("");
1953	pm->node.flags |= PM_UNSET;
1954	return &pm->node;
1955    }
1956
1957    for (gaptr = gs->array; gaptr < gs->array + gs->num; gaptr++) {
1958	if (!strcmp(name, gaptr->name)) {
1959	    char buf[DIGBUFSIZE];
1960
1961	    sprintf(buf, "%d", (int)gaptr->gid);
1962	    pm->u.str = dupstring(buf);
1963	    return &pm->node;
1964	}
1965    }
1966
1967    pm->u.str = dupstring("");
1968    pm->node.flags |= PM_UNSET;
1969    return &pm->node;
1970}
1971
1972/* Standard hash scan. */
1973
1974/**/
1975static void
1976scanpmusergroups(UNUSED(HashTable ht), ScanFunc func, int flags)
1977{
1978    struct param pm;
1979    Groupset gs = get_all_groups();
1980    Groupmap gaptr;
1981
1982    if (!gs) {
1983	zerr("failed to retrieve groups for user: %e", errno);
1984	return;
1985    }
1986
1987    memset((void *)&pm, 0, sizeof(pm));
1988    pm.node.flags = PM_SCALAR | PM_READONLY;
1989    pm.gsu.s = &nullsetscalar_gsu;
1990
1991    for (gaptr = gs->array; gaptr < gs->array + gs->num; gaptr++) {
1992	pm.node.nam = gaptr->name;
1993	if (func != scancountparams &&
1994	    ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
1995	     !(flags & SCANPM_WANTKEYS))) {
1996	    char buf[DIGBUFSIZE];
1997
1998	    sprintf(buf, "%d", (int)gaptr->gid);
1999	    pm.u.str = dupstring(buf);
2000	}
2001	func(&pm.node, flags);
2002    }
2003}
2004
2005
2006/* Table for defined parameters. */
2007
2008struct pardef {
2009    char *name;
2010    int flags;
2011    GetNodeFunc getnfn;
2012    ScanTabFunc scantfn;
2013    GsuHash hash_gsu;
2014    GsuArray array_gsu;
2015    Param pm;
2016};
2017
2018static const struct gsu_hash pmcommands_gsu =
2019{ hashgetfn, setpmcommands, stdunsetfn };
2020static const struct gsu_hash pmfunctions_gsu =
2021{ hashgetfn, setpmfunctions, stdunsetfn };
2022static const struct gsu_hash pmdisfunctions_gsu =
2023{ hashgetfn, setpmdisfunctions, stdunsetfn };
2024static const struct gsu_hash pmoptions_gsu =
2025{ hashgetfn, setpmoptions, stdunsetfn };
2026static const struct gsu_hash pmnameddirs_gsu =
2027{ hashgetfn, setpmnameddirs, stdunsetfn };
2028static const struct gsu_hash pmraliases_gsu =
2029{ hashgetfn, setpmraliases, stdunsetfn };
2030static const struct gsu_hash pmgaliases_gsu =
2031{ hashgetfn, setpmgaliases, stdunsetfn };
2032static const struct gsu_hash pmsaliases_gsu =
2033{ hashgetfn, setpmsaliases, stdunsetfn };
2034static const struct gsu_hash pmdisraliases_gsu =
2035{ hashgetfn, setpmdisraliases, stdunsetfn };
2036static const struct gsu_hash pmdisgaliases_gsu =
2037{ hashgetfn, setpmdisgaliases, stdunsetfn };
2038static const struct gsu_hash pmdissaliases_gsu =
2039{ hashgetfn, setpmdissaliases, stdunsetfn };
2040
2041static const struct gsu_array funcstack_gsu =
2042{ funcstackgetfn, arrsetfn, stdunsetfn };
2043static const struct gsu_array functrace_gsu =
2044{ functracegetfn, arrsetfn, stdunsetfn };
2045static const struct gsu_array funcsourcetrace_gsu =
2046{ funcsourcetracegetfn, arrsetfn, stdunsetfn };
2047static const struct gsu_array funcfiletrace_gsu =
2048{ funcfiletracegetfn, arrsetfn, stdunsetfn };
2049static const struct gsu_array reswords_gsu =
2050{ reswordsgetfn, arrsetfn, stdunsetfn };
2051static const struct gsu_array disreswords_gsu =
2052{ disreswordsgetfn, arrsetfn, stdunsetfn };
2053static const struct gsu_array patchars_gsu =
2054{ patcharsgetfn, arrsetfn, stdunsetfn };
2055static const struct gsu_array dispatchars_gsu =
2056{ dispatcharsgetfn, arrsetfn, stdunsetfn };
2057static const struct gsu_array dirs_gsu =
2058{ dirsgetfn, dirssetfn, stdunsetfn };
2059static const struct gsu_array historywords_gsu =
2060{ histwgetfn, arrsetfn, stdunsetfn };
2061
2062static struct paramdef partab[] = {
2063    SPECIALPMDEF("aliases", 0,
2064	    &pmraliases_gsu, getpmralias, scanpmraliases),
2065    SPECIALPMDEF("builtins", PM_READONLY, NULL, getpmbuiltin, scanpmbuiltins),
2066    SPECIALPMDEF("commands", 0, &pmcommands_gsu, getpmcommand, scanpmcommands),
2067    SPECIALPMDEF("dirstack", PM_ARRAY,
2068	    &dirs_gsu, NULL, NULL),
2069    SPECIALPMDEF("dis_aliases", 0,
2070	    &pmdisraliases_gsu, getpmdisralias, scanpmdisraliases),
2071    SPECIALPMDEF("dis_builtins", PM_READONLY,
2072	    NULL, getpmdisbuiltin, scanpmdisbuiltins),
2073    SPECIALPMDEF("dis_functions", 0,
2074	    &pmdisfunctions_gsu, getpmdisfunction, scanpmdisfunctions),
2075    SPECIALPMDEF("dis_galiases", 0,
2076	    &pmdisgaliases_gsu, getpmdisgalias, scanpmdisgaliases),
2077    SPECIALPMDEF("dis_patchars", PM_ARRAY|PM_READONLY,
2078	    &dispatchars_gsu, NULL, NULL),
2079    SPECIALPMDEF("dis_reswords", PM_ARRAY|PM_READONLY,
2080	    &disreswords_gsu, NULL, NULL),
2081    SPECIALPMDEF("dis_saliases", 0,
2082	    &pmdissaliases_gsu, getpmdissalias, scanpmdissaliases),
2083    SPECIALPMDEF("funcfiletrace", PM_ARRAY|PM_READONLY,
2084	    &funcfiletrace_gsu, NULL, NULL),
2085    SPECIALPMDEF("funcsourcetrace", PM_ARRAY|PM_READONLY,
2086	    &funcsourcetrace_gsu, NULL, NULL),
2087    SPECIALPMDEF("funcstack", PM_ARRAY|PM_READONLY,
2088	    &funcstack_gsu, NULL, NULL),
2089    SPECIALPMDEF("functions", 0, &pmfunctions_gsu, getpmfunction,
2090		 scanpmfunctions),
2091    SPECIALPMDEF("functrace", PM_ARRAY|PM_READONLY,
2092	    &functrace_gsu, NULL, NULL),
2093    SPECIALPMDEF("galiases", 0,
2094	    &pmgaliases_gsu, getpmgalias, scanpmgaliases),
2095    SPECIALPMDEF("history", PM_READONLY,
2096	    NULL, getpmhistory, scanpmhistory),
2097    SPECIALPMDEF("historywords", PM_ARRAY|PM_READONLY,
2098	    &historywords_gsu, NULL, NULL),
2099    SPECIALPMDEF("jobdirs", PM_READONLY,
2100	    NULL, getpmjobdir, scanpmjobdirs),
2101    SPECIALPMDEF("jobstates", PM_READONLY,
2102	    NULL, getpmjobstate, scanpmjobstates),
2103    SPECIALPMDEF("jobtexts", PM_READONLY,
2104	    NULL, getpmjobtext, scanpmjobtexts),
2105    SPECIALPMDEF("modules", PM_READONLY,
2106	    NULL, getpmmodule, scanpmmodules),
2107    SPECIALPMDEF("nameddirs", 0,
2108	    &pmnameddirs_gsu, getpmnameddir, scanpmnameddirs),
2109    SPECIALPMDEF("options", 0,
2110	    &pmoptions_gsu, getpmoption, scanpmoptions),
2111    SPECIALPMDEF("parameters", PM_READONLY,
2112	    NULL, getpmparameter, scanpmparameters),
2113    SPECIALPMDEF("patchars", PM_ARRAY|PM_READONLY,
2114	    &patchars_gsu, NULL, NULL),
2115    SPECIALPMDEF("reswords", PM_ARRAY|PM_READONLY,
2116	    &reswords_gsu, NULL, NULL),
2117    SPECIALPMDEF("saliases", 0,
2118	    &pmsaliases_gsu, getpmsalias, scanpmsaliases),
2119    SPECIALPMDEF("userdirs", PM_READONLY,
2120	    NULL, getpmuserdir, scanpmuserdirs),
2121    SPECIALPMDEF("usergroups", PM_READONLY,
2122	    NULL, getpmusergroups, scanpmusergroups)
2123};
2124
2125static struct features module_features = {
2126    NULL, 0,
2127    NULL, 0,
2128    NULL, 0,
2129    partab, sizeof(partab)/sizeof(*partab),
2130    0
2131};
2132
2133/**/
2134int
2135setup_(UNUSED(Module m))
2136{
2137    return 0;
2138}
2139
2140/**/
2141int
2142features_(Module m, char ***features)
2143{
2144    *features = featuresarray(m, &module_features);
2145    return 0;
2146}
2147
2148/**/
2149int
2150enables_(Module m, int **enables)
2151{
2152    int ret;
2153    /*
2154     * If we remove features, we shouldn't have an effect
2155     * on the main shell, so set the flag to indicate.
2156     */
2157    incleanup = 1;
2158    ret = handlefeatures(m, &module_features, enables);
2159    incleanup = 0;
2160    return ret;
2161}
2162
2163/**/
2164int
2165boot_(Module m)
2166{
2167    return 0;
2168}
2169
2170/**/
2171int
2172cleanup_(Module m)
2173{
2174    int ret;
2175    incleanup = 1;
2176    ret = setfeatureenables(m, &module_features, NULL);
2177    incleanup = 0;
2178    return ret;
2179}
2180
2181/**/
2182int
2183finish_(UNUSED(Module m))
2184{
2185    return 0;
2186}
2187