159243Sobrien/*
259243Sobrien * tw.init.c: Handle lists of things to complete
359243Sobrien */
459243Sobrien/*-
559243Sobrien * Copyright (c) 1980, 1991 The Regents of the University of California.
659243Sobrien * All rights reserved.
759243Sobrien *
859243Sobrien * Redistribution and use in source and binary forms, with or without
959243Sobrien * modification, are permitted provided that the following conditions
1059243Sobrien * are met:
1159243Sobrien * 1. Redistributions of source code must retain the above copyright
1259243Sobrien *    notice, this list of conditions and the following disclaimer.
1359243Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1459243Sobrien *    notice, this list of conditions and the following disclaimer in the
1559243Sobrien *    documentation and/or other materials provided with the distribution.
16100616Smp * 3. Neither the name of the University nor the names of its contributors
1759243Sobrien *    may be used to endorse or promote products derived from this software
1859243Sobrien *    without specific prior written permission.
1959243Sobrien *
2059243Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2159243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2259243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2359243Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2459243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2559243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2659243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2759243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2859243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2959243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3059243Sobrien * SUCH DAMAGE.
3159243Sobrien */
3259243Sobrien#include "sh.h"
3359243Sobrien#include "tw.h"
3459243Sobrien#include "ed.h"
3559243Sobrien#include "tc.h"
3659243Sobrien#include "sh.proc.h"
3759243Sobrien
3859243Sobrien#define TW_INCR	128
3959243Sobrien
4059243Sobrientypedef struct {
4159243Sobrien    Char **list, 			/* List of command names	*/
4259243Sobrien	  *buff;			/* Space holding command names	*/
43167465Smp    size_t nlist, 			/* Number of items		*/
4459243Sobrien           nbuff,			/* Current space in name buf	*/
4559243Sobrien           tlist,			/* Total space in list		*/
4659243Sobrien	   tbuff;			/* Total space in name buf	*/
4759243Sobrien} stringlist_t;
4859243Sobrien
4959243Sobrien
5059243Sobrienstatic struct varent *tw_vptr = NULL;	/* Current shell variable 	*/
5159243Sobrienstatic Char **tw_env = NULL;		/* Current environment variable */
52167465Smpstatic const Char *tw_word;		/* Current word pointer		*/
5359243Sobrienstatic struct KeyFuncs *tw_bind = NULL;	/* List of the bindings		*/
5459243Sobrien#ifndef HAVENOLIMIT
5559243Sobrienstatic struct limits *tw_limit = NULL;	/* List of the resource limits	*/
5659243Sobrien#endif /* HAVENOLIMIT */
5759243Sobrienstatic int tw_index = 0;		/* signal and job index		*/
5859243Sobrienstatic DIR   *tw_dir_fd = NULL;		/* Current directory descriptor	*/
5959243Sobrienstatic int    tw_cmd_got = 0;		/* What we need to do		*/
6059243Sobrienstatic stringlist_t tw_cmd  = { NULL, NULL, 0, 0, 0, 0 };
6159243Sobrienstatic stringlist_t tw_item = { NULL, NULL, 0, 0, 0, 0 };
6259243Sobrien#define TW_FL_CMD	0x01
6359243Sobrien#define TW_FL_ALIAS	0x02
6459243Sobrien#define TW_FL_BUILTIN	0x04
6559243Sobrien#define TW_FL_SORT	0x08
6659243Sobrien#define TW_FL_REL	0x10
6759243Sobrien
6859243Sobrienstatic struct {				/* Current element pointer	*/
69167465Smp    size_t cur;				/* Current element number	*/
7059243Sobrien    Char **pathv;			/* Current element in path	*/
7159243Sobrien    DIR   *dfd;				/* Current directory descriptor	*/
7259243Sobrien} tw_cmd_state;
7359243Sobrien
7459243Sobrien
7559243Sobrien#define SETDIR(dfd) \
7659243Sobrien    { \
7759243Sobrien	tw_dir_fd = dfd; \
7859243Sobrien	if (tw_dir_fd != NULL) \
7959243Sobrien	    rewinddir(tw_dir_fd); \
8059243Sobrien    }
8159243Sobrien
8259243Sobrien#define CLRDIR(dfd) \
8359243Sobrien    if (dfd != NULL) { \
84167465Smp	pintr_disabled++; \
85167465Smp	xclosedir(dfd); \
8659243Sobrien	dfd = NULL; \
87167465Smp	disabled_cleanup(&pintr_disabled); \
8859243Sobrien    }
8959243Sobrien
90167465Smpstatic Char	*tw_str_add		(stringlist_t *, size_t);
91167465Smpstatic void	 tw_str_free		(stringlist_t *);
92167465Smpstatic int       tw_dir_next		(struct Strbuf *, DIR *);
93167465Smpstatic void	 tw_cmd_add 		(const Char *name);
94167465Smpstatic void 	 tw_cmd_cmd		(void);
95167465Smpstatic void	 tw_cmd_builtin		(void);
96167465Smpstatic void	 tw_cmd_alias		(void);
97167465Smpstatic void	 tw_cmd_sort		(void);
98167465Smpstatic void 	 tw_vptr_start		(struct varent *);
9959243Sobrien
10059243Sobrien
10159243Sobrien/* tw_str_add():
10259243Sobrien *	Add an item to the string list
10359243Sobrien */
10459243Sobrienstatic Char *
105167465Smptw_str_add(stringlist_t *sl, size_t len)
10659243Sobrien{
10759243Sobrien    Char *ptr;
10859243Sobrien
10959243Sobrien    if (sl->tlist <= sl->nlist) {
110167465Smp	pintr_disabled++;
11159243Sobrien	sl->tlist += TW_INCR;
112167465Smp	sl->list = xrealloc(sl->list, sl->tlist * sizeof(Char *));
113167465Smp	disabled_cleanup(&pintr_disabled);
11459243Sobrien    }
11559243Sobrien    if (sl->tbuff <= sl->nbuff + len) {
116167465Smp	size_t i;
117167465Smp
11859243Sobrien	ptr = sl->buff;
119167465Smp	pintr_disabled++;
12059243Sobrien	sl->tbuff += TW_INCR + len;
121167465Smp	sl->buff = xrealloc(sl->buff, sl->tbuff * sizeof(Char));
12259243Sobrien	/* Re-thread the new pointer list, if changed */
12359243Sobrien	if (ptr != NULL && ptr != sl->buff) {
12459243Sobrien	    for (i = 0; i < sl->nlist; i++)
125316957Sdchagin		sl->list[i] = sl->buff + (sl->list[i] - ptr);
12659243Sobrien	}
127167465Smp	disabled_cleanup(&pintr_disabled);
12859243Sobrien    }
12959243Sobrien    ptr = sl->list[sl->nlist++] = &sl->buff[sl->nbuff];
13059243Sobrien    sl->nbuff += len;
13159243Sobrien    return ptr;
13259243Sobrien} /* tw_str_add */
13359243Sobrien
13459243Sobrien
13559243Sobrien/* tw_str_free():
13659243Sobrien *	Free a stringlist
13759243Sobrien */
13859243Sobrienstatic void
139167465Smptw_str_free(stringlist_t *sl)
14059243Sobrien{
141167465Smp    pintr_disabled++;
14259243Sobrien    if (sl->list) {
143167465Smp	xfree(sl->list);
14459243Sobrien	sl->list = NULL;
14559243Sobrien	sl->tlist = sl->nlist = 0;
14659243Sobrien    }
14759243Sobrien    if (sl->buff) {
148167465Smp	xfree(sl->buff);
14959243Sobrien	sl->buff = NULL;
15059243Sobrien	sl->tbuff = sl->nbuff = 0;
15159243Sobrien    }
152167465Smp    disabled_cleanup(&pintr_disabled);
15359243Sobrien} /* end tw_str_free */
15459243Sobrien
15559243Sobrien
156167465Smpstatic int
157167465Smptw_dir_next(struct Strbuf *res, DIR *dfd)
15859243Sobrien{
159145479Smp    struct dirent *dirp;
16059243Sobrien
16159243Sobrien    if (dfd == NULL)
162167465Smp	return 0;
16359243Sobrien
16459243Sobrien    if ((dirp = readdir(dfd)) != NULL) {
165167465Smp	Strbuf_append(res, str2short(dirp->d_name));
166167465Smp	return 1;
16759243Sobrien    }
168167465Smp    return 0;
16959243Sobrien} /* end tw_dir_next */
17059243Sobrien
17159243Sobrien
17259243Sobrien/* tw_cmd_add():
17359243Sobrien *	Add the name to the command list
17459243Sobrien */
17559243Sobrienstatic void
176167465Smptw_cmd_add(const Char *name)
17759243Sobrien{
178167465Smp    size_t len;
17959243Sobrien
180167465Smp    len = Strlen(name) + 2;
18159243Sobrien    (void) Strcpy(tw_str_add(&tw_cmd, len), name);
18259243Sobrien} /* end tw_cmd_add */
18359243Sobrien
18459243Sobrien
18559243Sobrien/* tw_cmd_free():
18659243Sobrien *	Free the command list
18759243Sobrien */
18859243Sobrienvoid
189167465Smptw_cmd_free(void)
19059243Sobrien{
19159243Sobrien    CLRDIR(tw_dir_fd)
19259243Sobrien    tw_str_free(&tw_cmd);
19359243Sobrien    tw_cmd_got = 0;
19459243Sobrien} /* end tw_cmd_free */
19559243Sobrien
19659243Sobrien/* tw_cmd_cmd():
19759243Sobrien *	Add system commands to the command list
19859243Sobrien */
19959243Sobrienstatic void
200167465Smptw_cmd_cmd(void)
20159243Sobrien{
202145479Smp    DIR *dirp;
203145479Smp    struct dirent *dp;
204145479Smp    Char *dir = NULL, *name;
205145479Smp    Char **pv;
20659243Sobrien    struct varent *v = adrof(STRpath);
20759243Sobrien    struct varent *recexec = adrof(STRrecognize_only_executables);
208167465Smp    size_t len;
20959243Sobrien
21059243Sobrien
211100616Smp    if (v == NULL || v->vec == NULL) /* if no path */
21259243Sobrien	return;
21359243Sobrien
21459243Sobrien    for (pv = v->vec; *pv; pv++) {
21559243Sobrien	if (pv[0][0] != '/') {
21659243Sobrien	    tw_cmd_got |= TW_FL_REL;
21759243Sobrien	    continue;
21859243Sobrien	}
21959243Sobrien
22059243Sobrien	if ((dirp = opendir(short2str(*pv))) == NULL)
22159243Sobrien	    continue;
22259243Sobrien
223167465Smp	cleanup_push(dirp, opendir_cleanup);
224167465Smp	if (recexec) {
22559243Sobrien	    dir = Strspl(*pv, STRslash);
226167465Smp	    cleanup_push(dir, xfree);
227167465Smp	}
22859243Sobrien	while ((dp = readdir(dirp)) != NULL) {
22969408Sache#if defined(_UWIN) || defined(__CYGWIN__)
23069408Sache	    /* Turn foo.{exe,com,bat} into foo since UWIN's readdir returns
23169408Sache	     * the file with the .exe, .com, .bat extension
232231990Smp	     *
233231990Smp	     * Same for Cygwin, but only for .exe and .com extension.
23469408Sache	     */
235167465Smp	    len = strlen(dp->d_name);
236167465Smp	    if (len > 4 && (strcmp(&dp->d_name[len - 4], ".exe") == 0 ||
237231990Smp#ifndef __CYGWIN__
238167465Smp		strcmp(&dp->d_name[len - 4], ".bat") == 0 ||
239231990Smp#endif /* !__CYGWIN__ */
240167465Smp		strcmp(&dp->d_name[len - 4], ".com") == 0))
241167465Smp		dp->d_name[len - 4] = '\0';
24269408Sache#endif /* _UWIN || __CYGWIN__ */
24359243Sobrien	    /* the call to executable() may make this a bit slow */
24459243Sobrien	    name = str2short(dp->d_name);
24559243Sobrien	    if (dp->d_ino == 0 || (recexec && !executable(dir, name, 0)))
24659243Sobrien		continue;
247167465Smp            len = Strlen(name);
24859243Sobrien            if (name[0] == '#' ||	/* emacs temp files	*/
24959243Sobrien		name[0] == '.' ||	/* .files		*/
250167465Smp		name[len - 1] == '~' ||	/* emacs backups	*/
251167465Smp		name[len - 1] == '%')	/* textedit backups	*/
25259243Sobrien                continue;		/* Ignore!		*/
25359243Sobrien            tw_cmd_add(name);
25459243Sobrien	}
255167465Smp	cleanup_until(dirp);
25659243Sobrien    }
25759243Sobrien} /* end tw_cmd_cmd */
25859243Sobrien
25959243Sobrien
26059243Sobrien/* tw_cmd_builtin():
26159243Sobrien *	Add builtins to the command list
26259243Sobrien */
26359243Sobrienstatic void
264167465Smptw_cmd_builtin(void)
26559243Sobrien{
266167465Smp    const struct biltins *bptr;
26759243Sobrien
26859243Sobrien    for (bptr = bfunc; bptr < &bfunc[nbfunc]; bptr++)
26959243Sobrien	if (bptr->bname)
27059243Sobrien	    tw_cmd_add(str2short(bptr->bname));
27169408Sache#ifdef WINNT_NATIVE
27259243Sobrien    for (bptr = nt_bfunc; bptr < &nt_bfunc[nt_nbfunc]; bptr++)
27359243Sobrien	if (bptr->bname)
27459243Sobrien	    tw_cmd_add(str2short(bptr->bname));
27569408Sache#endif /* WINNT_NATIVE*/
27659243Sobrien} /* end tw_cmd_builtin */
27759243Sobrien
27859243Sobrien
27959243Sobrien/* tw_cmd_alias():
28059243Sobrien *	Add aliases to the command list
28159243Sobrien */
28259243Sobrienstatic void
283167465Smptw_cmd_alias(void)
28459243Sobrien{
285145479Smp    struct varent *p;
286145479Smp    struct varent *c;
28759243Sobrien
28859243Sobrien    p = &aliases;
28959243Sobrien    for (;;) {
29059243Sobrien	while (p->v_left)
29159243Sobrien	    p = p->v_left;
29259243Sobrienx:
29359243Sobrien	if (p->v_parent == 0) /* is it the header? */
29459243Sobrien	    return;
29559243Sobrien	if (p->v_name)
29659243Sobrien	    tw_cmd_add(p->v_name);
29759243Sobrien	if (p->v_right) {
29859243Sobrien	    p = p->v_right;
29959243Sobrien	    continue;
30059243Sobrien	}
30159243Sobrien	do {
30259243Sobrien	    c = p;
30359243Sobrien	    p = p->v_parent;
30459243Sobrien	} while (p->v_right == c);
30559243Sobrien	goto x;
30659243Sobrien    }
30759243Sobrien} /* end tw_cmd_alias */
30859243Sobrien
30959243Sobrien
31059243Sobrien/* tw_cmd_sort():
31159243Sobrien *	Sort the command list removing duplicate elements
31259243Sobrien */
31359243Sobrienstatic void
314167465Smptw_cmd_sort(void)
31559243Sobrien{
316167465Smp    size_t fwd, i;
31759243Sobrien
318167465Smp    pintr_disabled++;
31959243Sobrien    /* sort the list. */
320167465Smp    qsort(tw_cmd.list, tw_cmd.nlist, sizeof(Char *), fcompare);
32159243Sobrien
32259243Sobrien    /* get rid of multiple entries */
323167465Smp    for (i = 0, fwd = 0; i + 1 < tw_cmd.nlist; i++) {
32459243Sobrien	if (Strcmp(tw_cmd.list[i], tw_cmd.list[i + 1]) == 0) /* garbage */
32559243Sobrien	    fwd++;		/* increase the forward ref. count */
32659243Sobrien	else if (fwd)
32759243Sobrien	    tw_cmd.list[i - fwd] = tw_cmd.list[i];
32859243Sobrien    }
32959243Sobrien    /* Fix fencepost error -- Theodore Ts'o <tytso@athena.mit.edu> */
33059243Sobrien    if (fwd)
33159243Sobrien	tw_cmd.list[i - fwd] = tw_cmd.list[i];
33259243Sobrien    tw_cmd.nlist -= fwd;
333167465Smp    disabled_cleanup(&pintr_disabled);
33459243Sobrien} /* end tw_cmd_sort */
33559243Sobrien
33659243Sobrien
33759243Sobrien/* tw_cmd_start():
33859243Sobrien *	Get the command list and sort it, if not done yet.
33959243Sobrien *	Reset the current pointer to the beginning of the command list
34059243Sobrien */
34159243Sobrien/*ARGSUSED*/
34259243Sobrienvoid
343167465Smptw_cmd_start(DIR *dfd, const Char *pat)
34459243Sobrien{
34559243Sobrien    static Char *defpath[] = { STRNULL, 0 };
34659243Sobrien    USE(pat);
34759243Sobrien    SETDIR(dfd)
34859243Sobrien    if ((tw_cmd_got & TW_FL_CMD) == 0) {
34959243Sobrien	tw_cmd_free();
35059243Sobrien	tw_cmd_cmd();
35159243Sobrien	tw_cmd_got |= TW_FL_CMD;
35259243Sobrien    }
35359243Sobrien    if ((tw_cmd_got & TW_FL_ALIAS) == 0) {
35459243Sobrien	tw_cmd_alias();
35559243Sobrien	tw_cmd_got &= ~TW_FL_SORT;
35659243Sobrien	tw_cmd_got |= TW_FL_ALIAS;
35759243Sobrien    }
35859243Sobrien    if ((tw_cmd_got & TW_FL_BUILTIN) == 0) {
35959243Sobrien	tw_cmd_builtin();
36059243Sobrien	tw_cmd_got &= ~TW_FL_SORT;
36159243Sobrien	tw_cmd_got |= TW_FL_BUILTIN;
36259243Sobrien    }
36359243Sobrien    if ((tw_cmd_got & TW_FL_SORT) == 0) {
36459243Sobrien	tw_cmd_sort();
36559243Sobrien	tw_cmd_got |= TW_FL_SORT;
36659243Sobrien    }
36759243Sobrien
36859243Sobrien    tw_cmd_state.cur = 0;
36959243Sobrien    CLRDIR(tw_cmd_state.dfd)
37059243Sobrien    if (tw_cmd_got & TW_FL_REL) {
37159243Sobrien	struct varent *vp = adrof(STRpath);
37259243Sobrien	if (vp && vp->vec)
37359243Sobrien	    tw_cmd_state.pathv = vp->vec;
37459243Sobrien	else
37559243Sobrien	    tw_cmd_state.pathv = defpath;
37659243Sobrien    }
37759243Sobrien    else
37859243Sobrien	tw_cmd_state.pathv = defpath;
37959243Sobrien} /* tw_cmd_start */
38059243Sobrien
38159243Sobrien
38259243Sobrien/* tw_cmd_next():
38359243Sobrien *	Return the next element in the command list or
38459243Sobrien *	Look for commands in the relative path components
38559243Sobrien */
386167465Smpint
387167465Smptw_cmd_next(struct Strbuf *res, struct Strbuf *dir, int *flags)
38859243Sobrien{
389167465Smp    int ret = 0;
390167465Smp    Char *ptr;
39159243Sobrien
39259243Sobrien    if (tw_cmd_state.cur < tw_cmd.nlist) {
39359243Sobrien	*flags = TW_DIR_OK;
394167465Smp	Strbuf_append(res, tw_cmd.list[tw_cmd_state.cur++]);
395167465Smp	return 1;
39659243Sobrien    }
39759243Sobrien
39859243Sobrien    /*
39959243Sobrien     * We need to process relatives in the path.
40059243Sobrien     */
401167465Smp    while ((tw_cmd_state.dfd == NULL ||
402231990Smp	    (res->len = 0, ret = tw_dir_next(res, tw_cmd_state.dfd)) == 0) &&
403167465Smp	   *tw_cmd_state.pathv != NULL) {
40459243Sobrien
40559243Sobrien        CLRDIR(tw_cmd_state.dfd)
40659243Sobrien
40759243Sobrien	while (*tw_cmd_state.pathv && tw_cmd_state.pathv[0][0] == '/')
40859243Sobrien	    tw_cmd_state.pathv++;
40959243Sobrien	if ((ptr = *tw_cmd_state.pathv) != 0) {
410231990Smp	    res->len = 0;
411167465Smp	    Strbuf_append(res, ptr);
412167465Smp	    ret = 1;
41359243Sobrien	    /*
41459243Sobrien	     * We complete directories only on '.' should that
41559243Sobrien	     * be changed?
41659243Sobrien	     */
417167465Smp	    dir->len = 0;
41859243Sobrien	    if (ptr[0] == '\0' || (ptr[0] == '.' && ptr[1] == '\0')) {
41959243Sobrien		tw_cmd_state.dfd = opendir(".");
420167465Smp		*flags = TW_DIR_OK | TW_EXEC_CHK;
42159243Sobrien	    }
42259243Sobrien	    else {
423167465Smp		Strbuf_append(dir, *tw_cmd_state.pathv);
424167465Smp		Strbuf_append1(dir, '/');
42559243Sobrien		tw_cmd_state.dfd = opendir(short2str(*tw_cmd_state.pathv));
42659243Sobrien		*flags = TW_EXEC_CHK;
42759243Sobrien	    }
428167465Smp	    Strbuf_terminate(dir);
42959243Sobrien	    tw_cmd_state.pathv++;
43059243Sobrien	}
43159243Sobrien    }
432167465Smp    return ret;
43359243Sobrien} /* end tw_cmd_next */
43459243Sobrien
43559243Sobrien
43659243Sobrien/* tw_vptr_start():
43759243Sobrien *	Find the first variable in the variable list
43859243Sobrien */
43959243Sobrienstatic void
440167465Smptw_vptr_start(struct varent *c)
44159243Sobrien{
44259243Sobrien    tw_vptr = c;		/* start at beginning of variable list */
44359243Sobrien
44459243Sobrien    for (;;) {
44559243Sobrien	while (tw_vptr->v_left)
44659243Sobrien	    tw_vptr = tw_vptr->v_left;
44759243Sobrienx:
44859243Sobrien	if (tw_vptr->v_parent == 0) {	/* is it the header? */
44959243Sobrien	    tw_vptr = NULL;
45059243Sobrien	    return;
45159243Sobrien	}
45259243Sobrien	if (tw_vptr->v_name)
45359243Sobrien	    return;		/* found first one */
45459243Sobrien	if (tw_vptr->v_right) {
45559243Sobrien	    tw_vptr = tw_vptr->v_right;
45659243Sobrien	    continue;
45759243Sobrien	}
45859243Sobrien	do {
45959243Sobrien	    c = tw_vptr;
46059243Sobrien	    tw_vptr = tw_vptr->v_parent;
46159243Sobrien	} while (tw_vptr->v_right == c);
46259243Sobrien	goto x;
46359243Sobrien    }
46459243Sobrien} /* end tw_shvar_start */
46559243Sobrien
46659243Sobrien
46759243Sobrien/* tw_shvar_next():
46859243Sobrien *	Return the next shell variable
46959243Sobrien */
47059243Sobrien/*ARGSUSED*/
471167465Smpint
472167465Smptw_shvar_next(struct Strbuf *res, struct Strbuf *dir, int *flags)
47359243Sobrien{
474145479Smp    struct varent *p;
475145479Smp    struct varent *c;
47659243Sobrien
47759243Sobrien    USE(flags);
47859243Sobrien    USE(dir);
47959243Sobrien    if ((p = tw_vptr) == NULL)
480167465Smp	return 0;		/* just in case */
48159243Sobrien
482167465Smp    Strbuf_append(res, p->v_name); /* we know that this name is here now */
48359243Sobrien
48459243Sobrien    /* now find the next one */
48559243Sobrien    for (;;) {
48659243Sobrien	if (p->v_right) {	/* if we can go right */
48759243Sobrien	    p = p->v_right;
48859243Sobrien	    while (p->v_left)
48959243Sobrien		p = p->v_left;
49059243Sobrien	}
49159243Sobrien	else {			/* else go up */
49259243Sobrien	    do {
49359243Sobrien		c = p;
49459243Sobrien		p = p->v_parent;
49559243Sobrien	    } while (p->v_right == c);
49659243Sobrien	}
49759243Sobrien	if (p->v_parent == 0) {	/* is it the header? */
49859243Sobrien	    tw_vptr = NULL;
499167465Smp	    return 1;
50059243Sobrien	}
50159243Sobrien	if (p->v_name) {
50259243Sobrien	    tw_vptr = p;	/* save state for the next call */
503167465Smp	    return 1;
50459243Sobrien	}
50559243Sobrien    }
50659243Sobrien} /* end tw_shvar_next */
50759243Sobrien
50859243Sobrien
50959243Sobrien/* tw_envvar_next():
51059243Sobrien *	Return the next environment variable
51159243Sobrien */
51259243Sobrien/*ARGSUSED*/
513167465Smpint
514167465Smptw_envvar_next(struct Strbuf *res, struct Strbuf *dir, int *flags)
51559243Sobrien{
516167465Smp    const Char *ps;
51759243Sobrien
51859243Sobrien    USE(flags);
51959243Sobrien    USE(dir);
52059243Sobrien    if (tw_env == NULL || *tw_env == NULL)
521167465Smp	return 0;
522167465Smp    for (ps = *tw_env; *ps && *ps != '='; ps++)
52359243Sobrien	continue;
524167465Smp    Strbuf_appendn(res, *tw_env, ps - *tw_env);
52559243Sobrien    tw_env++;
526167465Smp    return 1;
52759243Sobrien} /* end tw_envvar_next */
52859243Sobrien
52959243Sobrien
53059243Sobrien/* tw_var_start():
53159243Sobrien *	Begin the list of the shell and environment variables
53259243Sobrien */
53359243Sobrien/*ARGSUSED*/
53459243Sobrienvoid
535167465Smptw_var_start(DIR *dfd, const Char *pat)
53659243Sobrien{
53759243Sobrien    USE(pat);
53859243Sobrien    SETDIR(dfd)
53959243Sobrien    tw_vptr_start(&shvhed);
54059243Sobrien    tw_env = STR_environ;
54159243Sobrien} /* end tw_var_start */
54259243Sobrien
54359243Sobrien
54459243Sobrien/* tw_alias_start():
54559243Sobrien *	Begin the list of the shell aliases
54659243Sobrien */
54759243Sobrien/*ARGSUSED*/
54859243Sobrienvoid
549167465Smptw_alias_start(DIR *dfd, const Char *pat)
55059243Sobrien{
55159243Sobrien    USE(pat);
55259243Sobrien    SETDIR(dfd)
55359243Sobrien    tw_vptr_start(&aliases);
55459243Sobrien    tw_env = NULL;
55559243Sobrien} /* tw_alias_start */
55659243Sobrien
55759243Sobrien
55859243Sobrien/* tw_complete_start():
55959243Sobrien *	Begin the list of completions
56059243Sobrien */
56159243Sobrien/*ARGSUSED*/
56259243Sobrienvoid
563167465Smptw_complete_start(DIR *dfd, const Char *pat)
56459243Sobrien{
56559243Sobrien    USE(pat);
56659243Sobrien    SETDIR(dfd)
56759243Sobrien    tw_vptr_start(&completions);
56859243Sobrien    tw_env = NULL;
56959243Sobrien} /* end tw_complete_start */
57059243Sobrien
57159243Sobrien
57259243Sobrien/* tw_var_next():
57359243Sobrien *	Return the next shell or environment variable
57459243Sobrien */
575167465Smpint
576167465Smptw_var_next(struct Strbuf *res, struct Strbuf *dir, int *flags)
57759243Sobrien{
578167465Smp    int ret = 0;
57959243Sobrien
58059243Sobrien    if (tw_vptr)
581167465Smp	ret = tw_shvar_next(res, dir, flags);
582167465Smp    if (ret == 0 && tw_env)
583167465Smp	ret = tw_envvar_next(res, dir, flags);
584167465Smp    return ret;
58559243Sobrien} /* end tw_var_next */
58659243Sobrien
58759243Sobrien
58859243Sobrien/* tw_logname_start():
58959243Sobrien *	Initialize lognames to the beginning of the list
59059243Sobrien */
59159243Sobrien/*ARGSUSED*/
59259243Sobrienvoid
593167465Smptw_logname_start(DIR *dfd, const Char *pat)
59459243Sobrien{
59559243Sobrien    USE(pat);
59659243Sobrien    SETDIR(dfd)
597145479Smp#ifdef HAVE_GETPWENT
59859243Sobrien    (void) setpwent();	/* Open passwd file */
599145479Smp#endif
60059243Sobrien} /* end tw_logname_start */
60159243Sobrien
60259243Sobrien
60359243Sobrien/* tw_logname_next():
60459243Sobrien *	Return the next entry from the passwd file
60559243Sobrien */
60659243Sobrien/*ARGSUSED*/
607167465Smpint
608167465Smptw_logname_next(struct Strbuf *res, struct Strbuf *dir, int *flags)
60959243Sobrien{
61059243Sobrien    struct passwd *pw;
611167465Smp
61259243Sobrien    /*
61359243Sobrien     * We don't want to get interrupted inside getpwent()
61459243Sobrien     * because the yellow pages code is not interruptible,
61559243Sobrien     * and if we call endpwent() immediatetely after
61659243Sobrien     * (in pintr()) we may be freeing an invalid pointer
61759243Sobrien     */
61859243Sobrien    USE(flags);
61959243Sobrien    USE(dir);
620167465Smp    pintr_disabled++;
621145479Smp#ifdef HAVE_GETPWENT
622145479Smp    pw = getpwent();
623145479Smp#else
62459243Sobrien    pw = NULL;
625145479Smp#endif
626167465Smp    disabled_cleanup(&pintr_disabled);
62759243Sobrien
62859243Sobrien    if (pw == NULL) {
62959243Sobrien#ifdef YPBUGS
63059243Sobrien	fix_yp_bugs();
63159243Sobrien#endif
632167465Smp	return 0;
63359243Sobrien    }
634167465Smp    Strbuf_append(res, str2short(pw->pw_name));
635167465Smp    return 1;
63659243Sobrien} /* end tw_logname_next */
63759243Sobrien
63859243Sobrien
63959243Sobrien/* tw_logname_end():
64059243Sobrien *	Close the passwd file to finish the logname list
64159243Sobrien */
64259243Sobrienvoid
643167465Smptw_logname_end(void)
64459243Sobrien{
64559243Sobrien#ifdef YPBUGS
64659243Sobrien    fix_yp_bugs();
64759243Sobrien#endif
648145479Smp#ifdef HAVE_GETPWENT
64959243Sobrien   (void) endpwent();
650145479Smp#endif
65159243Sobrien} /* end tw_logname_end */
65259243Sobrien
65359243Sobrien
65459243Sobrien/* tw_grpname_start():
65559243Sobrien *	Initialize grpnames to the beginning of the list
65659243Sobrien */
65759243Sobrien/*ARGSUSED*/
65859243Sobrienvoid
659167465Smptw_grpname_start(DIR *dfd, const Char *pat)
66059243Sobrien{
66159243Sobrien    USE(pat);
66259243Sobrien    SETDIR(dfd)
663231990Smp#if !defined(_VMS_POSIX) && !defined(_OSD_POSIX) && !defined(WINNT_NATIVE) && !defined (__ANDROID__)
66459243Sobrien    (void) setgrent();	/* Open group file */
66569408Sache#endif /* !_VMS_POSIX && !_OSD_POSIX && !WINNT_NATIVE */
66659243Sobrien} /* end tw_grpname_start */
66759243Sobrien
66859243Sobrien
66959243Sobrien/* tw_grpname_next():
67059243Sobrien *	Return the next entry from the group file
67159243Sobrien */
67259243Sobrien/*ARGSUSED*/
673167465Smpint
674167465Smptw_grpname_next(struct Strbuf *res, struct Strbuf *dir, int *flags)
67559243Sobrien{
67659243Sobrien    struct group *gr;
677167465Smp
67859243Sobrien    /*
67959243Sobrien     * We don't want to get interrupted inside getgrent()
68059243Sobrien     * because the yellow pages code is not interruptible,
68159243Sobrien     * and if we call endgrent() immediatetely after
68259243Sobrien     * (in pintr()) we may be freeing an invalid pointer
68359243Sobrien     */
68459243Sobrien    USE(flags);
68559243Sobrien    USE(dir);
686167465Smp    pintr_disabled++;
687231990Smp#if !defined(_VMS_POSIX) && !defined(_OSD_POSIX) && !defined(WINNT_NATIVE) && !defined(__ANDROID__)
688167465Smp    errno = 0;
689167465Smp    while ((gr = getgrent()) == NULL && errno == EINTR) {
690167465Smp	handle_pending_signals();
691167465Smp	errno = 0;
692167465Smp    }
69369408Sache#else /* _VMS_POSIX || _OSD_POSIX || WINNT_NATIVE */
69459243Sobrien    gr = NULL;
69569408Sache#endif /* !_VMS_POSIX && !_OSD_POSIX && !WINNT_NATIVE */
696167465Smp    disabled_cleanup(&pintr_disabled);
69759243Sobrien
69859243Sobrien    if (gr == NULL) {
69959243Sobrien#ifdef YPBUGS
70059243Sobrien	fix_yp_bugs();
70159243Sobrien#endif
702167465Smp	return 0;
70359243Sobrien    }
704167465Smp    Strbuf_append(res, str2short(gr->gr_name));
705167465Smp    return 1;
70659243Sobrien} /* end tw_grpname_next */
70759243Sobrien
70859243Sobrien
70959243Sobrien/* tw_grpname_end():
71059243Sobrien *	Close the group file to finish the groupname list
71159243Sobrien */
71259243Sobrienvoid
713167465Smptw_grpname_end(void)
71459243Sobrien{
71559243Sobrien#ifdef YPBUGS
71659243Sobrien    fix_yp_bugs();
71759243Sobrien#endif
718231990Smp#if !defined(_VMS_POSIX) && !defined(_OSD_POSIX) && !defined(WINNT_NATIVE) && !defined (__ANDROID__)
71959243Sobrien   (void) endgrent();
72069408Sache#endif /* !_VMS_POSIX && !_OSD_POSIX && !WINNT_NATIVE */
72159243Sobrien} /* end tw_grpname_end */
72259243Sobrien
72359243Sobrien/* tw_file_start():
72459243Sobrien *	Initialize the directory for the file list
72559243Sobrien */
72659243Sobrien/*ARGSUSED*/
72759243Sobrienvoid
728167465Smptw_file_start(DIR *dfd, const Char *pat)
72959243Sobrien{
73059243Sobrien    struct varent *vp;
73159243Sobrien    USE(pat);
73259243Sobrien    SETDIR(dfd)
73359243Sobrien    if ((vp = adrof(STRcdpath)) != NULL)
73459243Sobrien	tw_env = vp->vec;
73559243Sobrien} /* end tw_file_start */
73659243Sobrien
73759243Sobrien
73859243Sobrien/* tw_file_next():
73959243Sobrien *	Return the next file in the directory
74059243Sobrien */
741167465Smpint
742167465Smptw_file_next(struct Strbuf *res, struct Strbuf *dir, int *flags)
74359243Sobrien{
744167465Smp    int ret = tw_dir_next(res, tw_dir_fd);
745167465Smp    if (ret == 0 && (*flags & TW_DIR_OK) != 0) {
74659243Sobrien	CLRDIR(tw_dir_fd)
74759243Sobrien	while (tw_env && *tw_env)
74859243Sobrien	    if ((tw_dir_fd = opendir(short2str(*tw_env))) != NULL)
74959243Sobrien		break;
75059243Sobrien	    else
75159243Sobrien		tw_env++;
752167465Smp
75359243Sobrien	if (tw_dir_fd) {
754167465Smp	    dir->len = 0;
755167465Smp	    Strbuf_append(dir, *tw_env++);
756167465Smp	    Strbuf_append1(dir, '/');
757167465Smp	    Strbuf_terminate(dir);
758167465Smp	    ret = tw_dir_next(res, tw_dir_fd);
75959243Sobrien	}
76059243Sobrien    }
761167465Smp    return ret;
76259243Sobrien} /* end tw_file_next */
76359243Sobrien
76459243Sobrien
76559243Sobrien/* tw_dir_end():
76659243Sobrien *	Clear directory related lists
76759243Sobrien */
76859243Sobrienvoid
769167465Smptw_dir_end(void)
77059243Sobrien{
77159243Sobrien   CLRDIR(tw_dir_fd)
77259243Sobrien   CLRDIR(tw_cmd_state.dfd)
77359243Sobrien} /* end tw_dir_end */
77459243Sobrien
77559243Sobrien
77659243Sobrien/* tw_item_free():
77759243Sobrien *	Free the item list
77859243Sobrien */
77959243Sobrienvoid
780167465Smptw_item_free(void)
78159243Sobrien{
78259243Sobrien    tw_str_free(&tw_item);
78359243Sobrien} /* end tw_item_free */
78459243Sobrien
78559243Sobrien
78659243Sobrien/* tw_item_get():
78759243Sobrien *	Return the list of items
78859243Sobrien */
78959243SobrienChar **
790167465Smptw_item_get(void)
79159243Sobrien{
79259243Sobrien    return tw_item.list;
79359243Sobrien} /* end tw_item_get */
79459243Sobrien
79559243Sobrien
79659243Sobrien/* tw_item_add():
797167465Smp *	Return a new item for a Strbuf_terminate()'d s
79859243Sobrien */
799167465Smpvoid
800167465Smptw_item_add(const struct Strbuf *s)
80159243Sobrien{
802167465Smp    Char *p;
803167465Smp
804167465Smp    p = tw_str_add(&tw_item, s->len + 1);
805167465Smp    Strcpy(p, s->s);
80659243Sobrien} /* tw_item_add */
80759243Sobrien
80859243Sobrien
80959243Sobrien/* tw_item_find():
81059243Sobrien *      Find the string if it exists in the item list
81159243Sobrien *	end return it.
81259243Sobrien */
81359243SobrienChar *
814167465Smptw_item_find(Char *str)
81559243Sobrien{
816167465Smp    size_t i;
81759243Sobrien
81859243Sobrien    if (tw_item.list == NULL || str == NULL)
81959243Sobrien	return NULL;
82059243Sobrien
82159243Sobrien    for (i = 0; i < tw_item.nlist; i++)
82259243Sobrien	if (tw_item.list[i] != NULL && Strcmp(tw_item.list[i], str) == 0)
82359243Sobrien	    return tw_item.list[i];
82459243Sobrien    return NULL;
82559243Sobrien} /* end tw_item_find */
82659243Sobrien
82759243Sobrien
82859243Sobrien/* tw_vl_start():
82959243Sobrien *	Initialize a variable list
83059243Sobrien */
83159243Sobrienvoid
832167465Smptw_vl_start(DIR *dfd, const Char *pat)
83359243Sobrien{
83459243Sobrien    SETDIR(dfd)
83559243Sobrien    if ((tw_vptr = adrof(pat)) != NULL) {
83659243Sobrien	tw_env = tw_vptr->vec;
83759243Sobrien	tw_vptr = NULL;
83859243Sobrien    }
83959243Sobrien    else
84059243Sobrien	tw_env = NULL;
84159243Sobrien} /* end tw_vl_start */
84259243Sobrien
84359243Sobrien
84459243Sobrien/*
84559243Sobrien * Initialize a word list
84659243Sobrien */
84759243Sobrienvoid
848167465Smptw_wl_start(DIR *dfd, const Char *pat)
84959243Sobrien{
85059243Sobrien    SETDIR(dfd);
85159243Sobrien    tw_word = pat;
85259243Sobrien} /* end tw_wl_start */
85359243Sobrien
85459243Sobrien
85559243Sobrien/*
85659243Sobrien * Return the next word from the word list
85759243Sobrien */
85859243Sobrien/*ARGSUSED*/
859167465Smpint
860167465Smptw_wl_next(struct Strbuf *res, struct Strbuf *dir, int *flags)
86159243Sobrien{
862167465Smp    const Char *p;
863167465Smp
864167465Smp    USE(dir);
86559243Sobrien    USE(flags);
86659243Sobrien    if (tw_word == NULL || tw_word[0] == '\0')
867167465Smp	return 0;
868167465Smp
86959243Sobrien    while (*tw_word && Isspace(*tw_word)) tw_word++;
87059243Sobrien
871167465Smp    for (p = tw_word; *tw_word && !Isspace(*tw_word); tw_word++)
87259243Sobrien	continue;
873167465Smp    if (tw_word == p)
874167465Smp	return 0;
875167465Smp    Strbuf_appendn(res, p, tw_word - p);
87659243Sobrien    if (*tw_word)
877167465Smp	tw_word++;
878167465Smp    return 1;
87959243Sobrien} /* end tw_wl_next */
88059243Sobrien
88159243Sobrien
88259243Sobrien/* tw_bind_start():
88359243Sobrien *	Begin the list of the shell bindings
88459243Sobrien */
88559243Sobrien/*ARGSUSED*/
88659243Sobrienvoid
887167465Smptw_bind_start(DIR *dfd, const Char *pat)
88859243Sobrien{
88959243Sobrien    USE(pat);
89059243Sobrien    SETDIR(dfd)
89159243Sobrien    tw_bind = FuncNames;
89259243Sobrien} /* end tw_bind_start */
89359243Sobrien
89459243Sobrien
89559243Sobrien/* tw_bind_next():
89659243Sobrien *	Begin the list of the shell bindings
89759243Sobrien */
89859243Sobrien/*ARGSUSED*/
899167465Smpint
900167465Smptw_bind_next(struct Strbuf *res, struct Strbuf *dir, int *flags)
90159243Sobrien{
902167465Smp    USE(dir);
90359243Sobrien    USE(flags);
90459243Sobrien    if (tw_bind && tw_bind->name) {
905167465Smp	const char *ptr;
906167465Smp
907167465Smp	for (ptr = tw_bind->name; *ptr != '\0'; ptr++)
908167465Smp	    Strbuf_append1(res, *ptr);
90959243Sobrien	tw_bind++;
910167465Smp	return 1;
91159243Sobrien    }
912167465Smp    return 0;
91359243Sobrien} /* end tw_bind_next */
91459243Sobrien
91559243Sobrien
91659243Sobrien/* tw_limit_start():
91759243Sobrien *	Begin the list of the shell limitings
91859243Sobrien */
91959243Sobrien/*ARGSUSED*/
92059243Sobrienvoid
921167465Smptw_limit_start(DIR *dfd, const Char *pat)
92259243Sobrien{
92359243Sobrien    USE(pat);
92459243Sobrien    SETDIR(dfd)
92559243Sobrien#ifndef HAVENOLIMIT
92659243Sobrien    tw_limit = limits;
92759243Sobrien#endif /* ! HAVENOLIMIT */
92859243Sobrien} /* end tw_limit_start */
92959243Sobrien
93059243Sobrien
93159243Sobrien/* tw_limit_next():
93259243Sobrien *	Begin the list of the shell limitings
93359243Sobrien */
93459243Sobrien/*ARGSUSED*/
935167465Smpint
936167465Smptw_limit_next(struct Strbuf *res, struct Strbuf *dir, int *flags)
93759243Sobrien{
938167465Smp    USE(dir);
939167465Smp    USE(flags);
94059243Sobrien#ifndef HAVENOLIMIT
94159243Sobrien    if (tw_limit && tw_limit->limname) {
942167465Smp	const char *ptr;
943167465Smp
944167465Smp	for (ptr = tw_limit->limname; *ptr != '\0'; ptr++)
945167465Smp	    Strbuf_append1(res, *ptr);
94659243Sobrien	tw_limit++;
947167465Smp	return 1;
94859243Sobrien    }
94959243Sobrien#endif /* ! HAVENOLIMIT */
950167465Smp    return 0;
95159243Sobrien} /* end tw_limit_next */
95259243Sobrien
95359243Sobrien
95459243Sobrien/* tw_sig_start():
95559243Sobrien *	Begin the list of the shell sigings
95659243Sobrien */
95759243Sobrien/*ARGSUSED*/
95859243Sobrienvoid
959167465Smptw_sig_start(DIR *dfd, const Char *pat)
96059243Sobrien{
96159243Sobrien    USE(pat);
96259243Sobrien    SETDIR(dfd)
96359243Sobrien    tw_index = 0;
96459243Sobrien} /* end tw_sig_start */
96559243Sobrien
96659243Sobrien
96759243Sobrien/* tw_sig_next():
96859243Sobrien *	Begin the list of the shell sigings
96959243Sobrien */
97059243Sobrien/*ARGSUSED*/
971167465Smpint
972167465Smptw_sig_next(struct Strbuf *res, struct Strbuf *dir, int *flags)
97359243Sobrien{
974167465Smp    USE(dir);
97559243Sobrien    USE(flags);
97659243Sobrien    for (;tw_index < nsig; tw_index++) {
977167465Smp	const char *ptr;
97859243Sobrien
97959243Sobrien	if (mesg[tw_index].iname == NULL)
98059243Sobrien	    continue;
98159243Sobrien
982167465Smp	for (ptr = mesg[tw_index].iname; *ptr != '\0'; ptr++)
983167465Smp	    Strbuf_append1(res, *ptr);
98459243Sobrien	tw_index++;
985167465Smp	return 1;
98659243Sobrien    }
987167465Smp    return 0;
98859243Sobrien} /* end tw_sig_next */
98959243Sobrien
99059243Sobrien
99159243Sobrien/* tw_job_start():
99259243Sobrien *	Begin the list of the shell jobings
99359243Sobrien */
99459243Sobrien/*ARGSUSED*/
99559243Sobrienvoid
996167465Smptw_job_start(DIR *dfd, const Char *pat)
99759243Sobrien{
99859243Sobrien    USE(pat);
99959243Sobrien    SETDIR(dfd)
100059243Sobrien    tw_index = 1;
100159243Sobrien} /* end tw_job_start */
100259243Sobrien
100359243Sobrien
100459243Sobrien/* tw_job_next():
100559243Sobrien *	Begin the list of the shell jobings
100659243Sobrien */
100759243Sobrien/*ARGSUSED*/
1008167465Smpint
1009167465Smptw_job_next(struct Strbuf *res, struct Strbuf *dir, int *flags)
101059243Sobrien{
101159243Sobrien    struct process *j;
101259243Sobrien
1013167465Smp    USE(dir);
101459243Sobrien    USE(flags);
101559243Sobrien    for (;tw_index <= pmaxindex; tw_index++) {
101659243Sobrien	for (j = proclist.p_next; j != NULL; j = j->p_next)
101759243Sobrien	    if (j->p_index == tw_index && j->p_procid == j->p_jobid)
101859243Sobrien		break;
101959243Sobrien	if (j == NULL)
102059243Sobrien	    continue;
1021167465Smp	Strbuf_append(res, j->p_command);
102259243Sobrien	tw_index++;
1023167465Smp	return 1;
102459243Sobrien    }
1025167465Smp    return 0;
102659243Sobrien} /* end tw_job_next */
1027