1/*
2 * tw.init.c: Handle lists of things to complete
3 */
4/*-
5 * Copyright (c) 1980, 1991 The Regents of the University of California.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32#include "sh.h"
33#include "tw.h"
34#include "ed.h"
35#include "tc.h"
36#include "sh.proc.h"
37
38#define TW_INCR	128
39
40typedef struct {
41    Char **list, 			/* List of command names	*/
42	  *buff;			/* Space holding command names	*/
43    size_t nlist, 			/* Number of items		*/
44           nbuff,			/* Current space in name buf	*/
45           tlist,			/* Total space in list		*/
46	   tbuff;			/* Total space in name buf	*/
47} stringlist_t;
48
49
50static struct varent *tw_vptr = NULL;	/* Current shell variable 	*/
51static Char **tw_env = NULL;		/* Current environment variable */
52static const Char *tw_word;		/* Current word pointer		*/
53static struct KeyFuncs *tw_bind = NULL;	/* List of the bindings		*/
54#ifndef HAVENOLIMIT
55static struct limits *tw_limit = NULL;	/* List of the resource limits	*/
56#endif /* HAVENOLIMIT */
57static int tw_index = 0;		/* signal and job index		*/
58static DIR   *tw_dir_fd = NULL;		/* Current directory descriptor	*/
59static int    tw_cmd_got = 0;		/* What we need to do		*/
60static stringlist_t tw_cmd  = { NULL, NULL, 0, 0, 0, 0 };
61static stringlist_t tw_item = { NULL, NULL, 0, 0, 0, 0 };
62#define TW_FL_CMD	0x01
63#define TW_FL_ALIAS	0x02
64#define TW_FL_BUILTIN	0x04
65#define TW_FL_SORT	0x08
66#define TW_FL_REL	0x10
67
68static struct {				/* Current element pointer	*/
69    size_t cur;				/* Current element number	*/
70    Char **pathv;			/* Current element in path	*/
71    DIR   *dfd;				/* Current directory descriptor	*/
72} tw_cmd_state;
73
74
75#define SETDIR(dfd) \
76    { \
77	tw_dir_fd = dfd; \
78	if (tw_dir_fd != NULL) \
79	    rewinddir(tw_dir_fd); \
80    }
81
82#define CLRDIR(dfd) \
83    if (dfd != NULL) { \
84	pintr_disabled++; \
85	xclosedir(dfd); \
86	dfd = NULL; \
87	disabled_cleanup(&pintr_disabled); \
88    }
89
90static Char	*tw_str_add		(stringlist_t *, size_t);
91static void	 tw_str_free		(stringlist_t *);
92static int       tw_dir_next		(struct Strbuf *, DIR *);
93static void	 tw_cmd_add 		(const Char *name);
94static void 	 tw_cmd_cmd		(void);
95static void	 tw_cmd_builtin		(void);
96static void	 tw_cmd_alias		(void);
97static void	 tw_cmd_sort		(void);
98static void 	 tw_vptr_start		(struct varent *);
99
100
101/* tw_str_add():
102 *	Add an item to the string list
103 */
104static Char *
105tw_str_add(stringlist_t *sl, size_t len)
106{
107    Char *ptr;
108
109    if (sl->tlist <= sl->nlist) {
110	pintr_disabled++;
111	sl->tlist += TW_INCR;
112	sl->list = xrealloc(sl->list, sl->tlist * sizeof(Char *));
113	disabled_cleanup(&pintr_disabled);
114    }
115    if (sl->tbuff <= sl->nbuff + len) {
116	size_t i;
117
118	ptr = sl->buff;
119	pintr_disabled++;
120	sl->tbuff += TW_INCR + len;
121	sl->buff = xrealloc(sl->buff, sl->tbuff * sizeof(Char));
122	/* Re-thread the new pointer list, if changed */
123	if (ptr != NULL && ptr != sl->buff) {
124	    for (i = 0; i < sl->nlist; i++)
125		sl->list[i] = sl->buff + (sl->list[i] - ptr);
126	}
127	disabled_cleanup(&pintr_disabled);
128    }
129    ptr = sl->list[sl->nlist++] = &sl->buff[sl->nbuff];
130    sl->nbuff += len;
131    return ptr;
132} /* tw_str_add */
133
134
135/* tw_str_free():
136 *	Free a stringlist
137 */
138static void
139tw_str_free(stringlist_t *sl)
140{
141    pintr_disabled++;
142    if (sl->list) {
143	xfree(sl->list);
144	sl->list = NULL;
145	sl->tlist = sl->nlist = 0;
146    }
147    if (sl->buff) {
148	xfree(sl->buff);
149	sl->buff = NULL;
150	sl->tbuff = sl->nbuff = 0;
151    }
152    disabled_cleanup(&pintr_disabled);
153} /* end tw_str_free */
154
155
156static int
157tw_dir_next(struct Strbuf *res, DIR *dfd)
158{
159    struct dirent *dirp;
160
161    if (dfd == NULL)
162	return 0;
163
164    if ((dirp = readdir(dfd)) != NULL) {
165	Strbuf_append(res, str2short(dirp->d_name));
166	return 1;
167    }
168    return 0;
169} /* end tw_dir_next */
170
171
172/* tw_cmd_add():
173 *	Add the name to the command list
174 */
175static void
176tw_cmd_add(const Char *name)
177{
178    size_t len;
179
180    len = Strlen(name) + 2;
181    (void) Strcpy(tw_str_add(&tw_cmd, len), name);
182} /* end tw_cmd_add */
183
184
185/* tw_cmd_free():
186 *	Free the command list
187 */
188void
189tw_cmd_free(void)
190{
191    CLRDIR(tw_dir_fd)
192    tw_str_free(&tw_cmd);
193    tw_cmd_got = 0;
194} /* end tw_cmd_free */
195
196/* tw_cmd_cmd():
197 *	Add system commands to the command list
198 */
199static void
200tw_cmd_cmd(void)
201{
202    DIR *dirp;
203    struct dirent *dp;
204    Char *dir = NULL, *name;
205    Char **pv;
206    struct varent *v = adrof(STRpath);
207    struct varent *recexec = adrof(STRrecognize_only_executables);
208    size_t len;
209
210
211    if (v == NULL || v->vec == NULL) /* if no path */
212	return;
213
214    for (pv = v->vec; *pv; pv++) {
215	if (pv[0][0] != '/') {
216	    tw_cmd_got |= TW_FL_REL;
217	    continue;
218	}
219
220	if ((dirp = opendir(short2str(*pv))) == NULL)
221	    continue;
222
223	cleanup_push(dirp, opendir_cleanup);
224	if (recexec) {
225	    dir = Strspl(*pv, STRslash);
226	    cleanup_push(dir, xfree);
227	}
228	while ((dp = readdir(dirp)) != NULL) {
229#if defined(_UWIN) || defined(__CYGWIN__)
230	    /* Turn foo.{exe,com,bat} into foo since UWIN's readdir returns
231	     * the file with the .exe, .com, .bat extension
232	     *
233	     * Same for Cygwin, but only for .exe and .com extension.
234	     */
235	    len = strlen(dp->d_name);
236	    if (len > 4 && (strcmp(&dp->d_name[len - 4], ".exe") == 0 ||
237#ifndef __CYGWIN__
238		strcmp(&dp->d_name[len - 4], ".bat") == 0 ||
239#endif /* !__CYGWIN__ */
240		strcmp(&dp->d_name[len - 4], ".com") == 0))
241		dp->d_name[len - 4] = '\0';
242#endif /* _UWIN || __CYGWIN__ */
243	    /* the call to executable() may make this a bit slow */
244	    name = str2short(dp->d_name);
245	    if (dp->d_ino == 0 || (recexec && !executable(dir, name, 0)))
246		continue;
247            len = Strlen(name);
248            if (name[0] == '#' ||	/* emacs temp files	*/
249		name[0] == '.' ||	/* .files		*/
250		name[len - 1] == '~' ||	/* emacs backups	*/
251		name[len - 1] == '%')	/* textedit backups	*/
252                continue;		/* Ignore!		*/
253            tw_cmd_add(name);
254	}
255	cleanup_until(dirp);
256    }
257} /* end tw_cmd_cmd */
258
259
260/* tw_cmd_builtin():
261 *	Add builtins to the command list
262 */
263static void
264tw_cmd_builtin(void)
265{
266    const struct biltins *bptr;
267
268    for (bptr = bfunc; bptr < &bfunc[nbfunc]; bptr++)
269	if (bptr->bname)
270	    tw_cmd_add(str2short(bptr->bname));
271#ifdef WINNT_NATIVE
272    for (bptr = nt_bfunc; bptr < &nt_bfunc[nt_nbfunc]; bptr++)
273	if (bptr->bname)
274	    tw_cmd_add(str2short(bptr->bname));
275#endif /* WINNT_NATIVE*/
276} /* end tw_cmd_builtin */
277
278
279/* tw_cmd_alias():
280 *	Add aliases to the command list
281 */
282static void
283tw_cmd_alias(void)
284{
285    struct varent *p;
286    struct varent *c;
287
288    p = &aliases;
289    for (;;) {
290	while (p->v_left)
291	    p = p->v_left;
292x:
293	if (p->v_parent == 0) /* is it the header? */
294	    return;
295	if (p->v_name)
296	    tw_cmd_add(p->v_name);
297	if (p->v_right) {
298	    p = p->v_right;
299	    continue;
300	}
301	do {
302	    c = p;
303	    p = p->v_parent;
304	} while (p->v_right == c);
305	goto x;
306    }
307} /* end tw_cmd_alias */
308
309
310/* tw_cmd_sort():
311 *	Sort the command list removing duplicate elements
312 */
313static void
314tw_cmd_sort(void)
315{
316    size_t fwd, i;
317
318    pintr_disabled++;
319    /* sort the list. */
320    qsort(tw_cmd.list, tw_cmd.nlist, sizeof(Char *), fcompare);
321
322    /* get rid of multiple entries */
323    for (i = 0, fwd = 0; i + 1 < tw_cmd.nlist; i++) {
324	if (Strcmp(tw_cmd.list[i], tw_cmd.list[i + 1]) == 0) /* garbage */
325	    fwd++;		/* increase the forward ref. count */
326	else if (fwd)
327	    tw_cmd.list[i - fwd] = tw_cmd.list[i];
328    }
329    /* Fix fencepost error -- Theodore Ts'o <tytso@athena.mit.edu> */
330    if (fwd)
331	tw_cmd.list[i - fwd] = tw_cmd.list[i];
332    tw_cmd.nlist -= fwd;
333    disabled_cleanup(&pintr_disabled);
334} /* end tw_cmd_sort */
335
336
337/* tw_cmd_start():
338 *	Get the command list and sort it, if not done yet.
339 *	Reset the current pointer to the beginning of the command list
340 */
341/*ARGSUSED*/
342void
343tw_cmd_start(DIR *dfd, const Char *pat)
344{
345    static Char *defpath[] = { STRNULL, 0 };
346    USE(pat);
347    SETDIR(dfd)
348    if ((tw_cmd_got & TW_FL_CMD) == 0) {
349	tw_cmd_free();
350	tw_cmd_cmd();
351	tw_cmd_got |= TW_FL_CMD;
352    }
353    if ((tw_cmd_got & TW_FL_ALIAS) == 0) {
354	tw_cmd_alias();
355	tw_cmd_got &= ~TW_FL_SORT;
356	tw_cmd_got |= TW_FL_ALIAS;
357    }
358    if ((tw_cmd_got & TW_FL_BUILTIN) == 0) {
359	tw_cmd_builtin();
360	tw_cmd_got &= ~TW_FL_SORT;
361	tw_cmd_got |= TW_FL_BUILTIN;
362    }
363    if ((tw_cmd_got & TW_FL_SORT) == 0) {
364	tw_cmd_sort();
365	tw_cmd_got |= TW_FL_SORT;
366    }
367
368    tw_cmd_state.cur = 0;
369    CLRDIR(tw_cmd_state.dfd)
370    if (tw_cmd_got & TW_FL_REL) {
371	struct varent *vp = adrof(STRpath);
372	if (vp && vp->vec)
373	    tw_cmd_state.pathv = vp->vec;
374	else
375	    tw_cmd_state.pathv = defpath;
376    }
377    else
378	tw_cmd_state.pathv = defpath;
379} /* tw_cmd_start */
380
381
382/* tw_cmd_next():
383 *	Return the next element in the command list or
384 *	Look for commands in the relative path components
385 */
386int
387tw_cmd_next(struct Strbuf *res, struct Strbuf *dir, int *flags)
388{
389    int ret = 0;
390    Char *ptr;
391
392    if (tw_cmd_state.cur < tw_cmd.nlist) {
393	*flags = TW_DIR_OK;
394	Strbuf_append(res, tw_cmd.list[tw_cmd_state.cur++]);
395	return 1;
396    }
397
398    /*
399     * We need to process relatives in the path.
400     */
401    while ((tw_cmd_state.dfd == NULL ||
402	    (res->len = 0, ret = tw_dir_next(res, tw_cmd_state.dfd)) == 0) &&
403	   *tw_cmd_state.pathv != NULL) {
404
405        CLRDIR(tw_cmd_state.dfd)
406
407	while (*tw_cmd_state.pathv && tw_cmd_state.pathv[0][0] == '/')
408	    tw_cmd_state.pathv++;
409	if ((ptr = *tw_cmd_state.pathv) != 0) {
410	    res->len = 0;
411	    Strbuf_append(res, ptr);
412	    ret = 1;
413	    /*
414	     * We complete directories only on '.' should that
415	     * be changed?
416	     */
417	    dir->len = 0;
418	    if (ptr[0] == '\0' || (ptr[0] == '.' && ptr[1] == '\0')) {
419		tw_cmd_state.dfd = opendir(".");
420		*flags = TW_DIR_OK | TW_EXEC_CHK;
421	    }
422	    else {
423		Strbuf_append(dir, *tw_cmd_state.pathv);
424		Strbuf_append1(dir, '/');
425		tw_cmd_state.dfd = opendir(short2str(*tw_cmd_state.pathv));
426		*flags = TW_EXEC_CHK;
427	    }
428	    Strbuf_terminate(dir);
429	    tw_cmd_state.pathv++;
430	}
431    }
432    return ret;
433} /* end tw_cmd_next */
434
435
436/* tw_vptr_start():
437 *	Find the first variable in the variable list
438 */
439static void
440tw_vptr_start(struct varent *c)
441{
442    tw_vptr = c;		/* start at beginning of variable list */
443
444    for (;;) {
445	while (tw_vptr->v_left)
446	    tw_vptr = tw_vptr->v_left;
447x:
448	if (tw_vptr->v_parent == 0) {	/* is it the header? */
449	    tw_vptr = NULL;
450	    return;
451	}
452	if (tw_vptr->v_name)
453	    return;		/* found first one */
454	if (tw_vptr->v_right) {
455	    tw_vptr = tw_vptr->v_right;
456	    continue;
457	}
458	do {
459	    c = tw_vptr;
460	    tw_vptr = tw_vptr->v_parent;
461	} while (tw_vptr->v_right == c);
462	goto x;
463    }
464} /* end tw_shvar_start */
465
466
467/* tw_shvar_next():
468 *	Return the next shell variable
469 */
470/*ARGSUSED*/
471int
472tw_shvar_next(struct Strbuf *res, struct Strbuf *dir, int *flags)
473{
474    struct varent *p;
475    struct varent *c;
476
477    USE(flags);
478    USE(dir);
479    if ((p = tw_vptr) == NULL)
480	return 0;		/* just in case */
481
482    Strbuf_append(res, p->v_name); /* we know that this name is here now */
483
484    /* now find the next one */
485    for (;;) {
486	if (p->v_right) {	/* if we can go right */
487	    p = p->v_right;
488	    while (p->v_left)
489		p = p->v_left;
490	}
491	else {			/* else go up */
492	    do {
493		c = p;
494		p = p->v_parent;
495	    } while (p->v_right == c);
496	}
497	if (p->v_parent == 0) {	/* is it the header? */
498	    tw_vptr = NULL;
499	    return 1;
500	}
501	if (p->v_name) {
502	    tw_vptr = p;	/* save state for the next call */
503	    return 1;
504	}
505    }
506} /* end tw_shvar_next */
507
508
509/* tw_envvar_next():
510 *	Return the next environment variable
511 */
512/*ARGSUSED*/
513int
514tw_envvar_next(struct Strbuf *res, struct Strbuf *dir, int *flags)
515{
516    const Char *ps;
517
518    USE(flags);
519    USE(dir);
520    if (tw_env == NULL || *tw_env == NULL)
521	return 0;
522    for (ps = *tw_env; *ps && *ps != '='; ps++)
523	continue;
524    Strbuf_appendn(res, *tw_env, ps - *tw_env);
525    tw_env++;
526    return 1;
527} /* end tw_envvar_next */
528
529
530/* tw_var_start():
531 *	Begin the list of the shell and environment variables
532 */
533/*ARGSUSED*/
534void
535tw_var_start(DIR *dfd, const Char *pat)
536{
537    USE(pat);
538    SETDIR(dfd)
539    tw_vptr_start(&shvhed);
540    tw_env = STR_environ;
541} /* end tw_var_start */
542
543
544/* tw_alias_start():
545 *	Begin the list of the shell aliases
546 */
547/*ARGSUSED*/
548void
549tw_alias_start(DIR *dfd, const Char *pat)
550{
551    USE(pat);
552    SETDIR(dfd)
553    tw_vptr_start(&aliases);
554    tw_env = NULL;
555} /* tw_alias_start */
556
557
558/* tw_complete_start():
559 *	Begin the list of completions
560 */
561/*ARGSUSED*/
562void
563tw_complete_start(DIR *dfd, const Char *pat)
564{
565    USE(pat);
566    SETDIR(dfd)
567    tw_vptr_start(&completions);
568    tw_env = NULL;
569} /* end tw_complete_start */
570
571
572/* tw_var_next():
573 *	Return the next shell or environment variable
574 */
575int
576tw_var_next(struct Strbuf *res, struct Strbuf *dir, int *flags)
577{
578    int ret = 0;
579
580    if (tw_vptr)
581	ret = tw_shvar_next(res, dir, flags);
582    if (ret == 0 && tw_env)
583	ret = tw_envvar_next(res, dir, flags);
584    return ret;
585} /* end tw_var_next */
586
587
588/* tw_logname_start():
589 *	Initialize lognames to the beginning of the list
590 */
591/*ARGSUSED*/
592void
593tw_logname_start(DIR *dfd, const Char *pat)
594{
595    USE(pat);
596    SETDIR(dfd)
597#ifdef HAVE_GETPWENT
598    (void) setpwent();	/* Open passwd file */
599#endif
600} /* end tw_logname_start */
601
602
603/* tw_logname_next():
604 *	Return the next entry from the passwd file
605 */
606/*ARGSUSED*/
607int
608tw_logname_next(struct Strbuf *res, struct Strbuf *dir, int *flags)
609{
610    struct passwd *pw;
611
612    /*
613     * We don't want to get interrupted inside getpwent()
614     * because the yellow pages code is not interruptible,
615     * and if we call endpwent() immediatetely after
616     * (in pintr()) we may be freeing an invalid pointer
617     */
618    USE(flags);
619    USE(dir);
620    pintr_disabled++;
621#ifdef HAVE_GETPWENT
622    pw = getpwent();
623#else
624    pw = NULL;
625#endif
626    disabled_cleanup(&pintr_disabled);
627
628    if (pw == NULL) {
629#ifdef YPBUGS
630	fix_yp_bugs();
631#endif
632	return 0;
633    }
634    Strbuf_append(res, str2short(pw->pw_name));
635    return 1;
636} /* end tw_logname_next */
637
638
639/* tw_logname_end():
640 *	Close the passwd file to finish the logname list
641 */
642void
643tw_logname_end(void)
644{
645#ifdef YPBUGS
646    fix_yp_bugs();
647#endif
648#ifdef HAVE_GETPWENT
649   (void) endpwent();
650#endif
651} /* end tw_logname_end */
652
653
654/* tw_grpname_start():
655 *	Initialize grpnames to the beginning of the list
656 */
657/*ARGSUSED*/
658void
659tw_grpname_start(DIR *dfd, const Char *pat)
660{
661    USE(pat);
662    SETDIR(dfd)
663#if !defined(_VMS_POSIX) && !defined(_OSD_POSIX) && !defined(WINNT_NATIVE) && !defined (__ANDROID__)
664    (void) setgrent();	/* Open group file */
665#endif /* !_VMS_POSIX && !_OSD_POSIX && !WINNT_NATIVE */
666} /* end tw_grpname_start */
667
668
669/* tw_grpname_next():
670 *	Return the next entry from the group file
671 */
672/*ARGSUSED*/
673int
674tw_grpname_next(struct Strbuf *res, struct Strbuf *dir, int *flags)
675{
676    struct group *gr;
677
678    /*
679     * We don't want to get interrupted inside getgrent()
680     * because the yellow pages code is not interruptible,
681     * and if we call endgrent() immediatetely after
682     * (in pintr()) we may be freeing an invalid pointer
683     */
684    USE(flags);
685    USE(dir);
686    pintr_disabled++;
687#if !defined(_VMS_POSIX) && !defined(_OSD_POSIX) && !defined(WINNT_NATIVE) && !defined(__ANDROID__)
688    errno = 0;
689    while ((gr = getgrent()) == NULL && errno == EINTR) {
690	handle_pending_signals();
691	errno = 0;
692    }
693#else /* _VMS_POSIX || _OSD_POSIX || WINNT_NATIVE */
694    gr = NULL;
695#endif /* !_VMS_POSIX && !_OSD_POSIX && !WINNT_NATIVE */
696    disabled_cleanup(&pintr_disabled);
697
698    if (gr == NULL) {
699#ifdef YPBUGS
700	fix_yp_bugs();
701#endif
702	return 0;
703    }
704    Strbuf_append(res, str2short(gr->gr_name));
705    return 1;
706} /* end tw_grpname_next */
707
708
709/* tw_grpname_end():
710 *	Close the group file to finish the groupname list
711 */
712void
713tw_grpname_end(void)
714{
715#ifdef YPBUGS
716    fix_yp_bugs();
717#endif
718#if !defined(_VMS_POSIX) && !defined(_OSD_POSIX) && !defined(WINNT_NATIVE) && !defined (__ANDROID__)
719   (void) endgrent();
720#endif /* !_VMS_POSIX && !_OSD_POSIX && !WINNT_NATIVE */
721} /* end tw_grpname_end */
722
723/* tw_file_start():
724 *	Initialize the directory for the file list
725 */
726/*ARGSUSED*/
727void
728tw_file_start(DIR *dfd, const Char *pat)
729{
730    struct varent *vp;
731    USE(pat);
732    SETDIR(dfd)
733    if ((vp = adrof(STRcdpath)) != NULL)
734	tw_env = vp->vec;
735} /* end tw_file_start */
736
737
738/* tw_file_next():
739 *	Return the next file in the directory
740 */
741int
742tw_file_next(struct Strbuf *res, struct Strbuf *dir, int *flags)
743{
744    int ret = tw_dir_next(res, tw_dir_fd);
745    if (ret == 0 && (*flags & TW_DIR_OK) != 0) {
746	CLRDIR(tw_dir_fd)
747	while (tw_env && *tw_env)
748	    if ((tw_dir_fd = opendir(short2str(*tw_env))) != NULL)
749		break;
750	    else
751		tw_env++;
752
753	if (tw_dir_fd) {
754	    dir->len = 0;
755	    Strbuf_append(dir, *tw_env++);
756	    Strbuf_append1(dir, '/');
757	    Strbuf_terminate(dir);
758	    ret = tw_dir_next(res, tw_dir_fd);
759	}
760    }
761    return ret;
762} /* end tw_file_next */
763
764
765/* tw_dir_end():
766 *	Clear directory related lists
767 */
768void
769tw_dir_end(void)
770{
771   CLRDIR(tw_dir_fd)
772   CLRDIR(tw_cmd_state.dfd)
773} /* end tw_dir_end */
774
775
776/* tw_item_free():
777 *	Free the item list
778 */
779void
780tw_item_free(void)
781{
782    tw_str_free(&tw_item);
783} /* end tw_item_free */
784
785
786/* tw_item_get():
787 *	Return the list of items
788 */
789Char **
790tw_item_get(void)
791{
792    return tw_item.list;
793} /* end tw_item_get */
794
795
796/* tw_item_add():
797 *	Return a new item for a Strbuf_terminate()'d s
798 */
799void
800tw_item_add(const struct Strbuf *s)
801{
802    Char *p;
803
804    p = tw_str_add(&tw_item, s->len + 1);
805    Strcpy(p, s->s);
806} /* tw_item_add */
807
808
809/* tw_item_find():
810 *      Find the string if it exists in the item list
811 *	end return it.
812 */
813Char *
814tw_item_find(Char *str)
815{
816    size_t i;
817
818    if (tw_item.list == NULL || str == NULL)
819	return NULL;
820
821    for (i = 0; i < tw_item.nlist; i++)
822	if (tw_item.list[i] != NULL && Strcmp(tw_item.list[i], str) == 0)
823	    return tw_item.list[i];
824    return NULL;
825} /* end tw_item_find */
826
827
828/* tw_vl_start():
829 *	Initialize a variable list
830 */
831void
832tw_vl_start(DIR *dfd, const Char *pat)
833{
834    SETDIR(dfd)
835    if ((tw_vptr = adrof(pat)) != NULL) {
836	tw_env = tw_vptr->vec;
837	tw_vptr = NULL;
838    }
839    else
840	tw_env = NULL;
841} /* end tw_vl_start */
842
843
844/*
845 * Initialize a word list
846 */
847void
848tw_wl_start(DIR *dfd, const Char *pat)
849{
850    SETDIR(dfd);
851    tw_word = pat;
852} /* end tw_wl_start */
853
854
855/*
856 * Return the next word from the word list
857 */
858/*ARGSUSED*/
859int
860tw_wl_next(struct Strbuf *res, struct Strbuf *dir, int *flags)
861{
862    const Char *p;
863
864    USE(dir);
865    USE(flags);
866    if (tw_word == NULL || tw_word[0] == '\0')
867	return 0;
868
869    while (*tw_word && Isspace(*tw_word)) tw_word++;
870
871    for (p = tw_word; *tw_word && !Isspace(*tw_word); tw_word++)
872	continue;
873    if (tw_word == p)
874	return 0;
875    Strbuf_appendn(res, p, tw_word - p);
876    if (*tw_word)
877	tw_word++;
878    return 1;
879} /* end tw_wl_next */
880
881
882/* tw_bind_start():
883 *	Begin the list of the shell bindings
884 */
885/*ARGSUSED*/
886void
887tw_bind_start(DIR *dfd, const Char *pat)
888{
889    USE(pat);
890    SETDIR(dfd)
891    tw_bind = FuncNames;
892} /* end tw_bind_start */
893
894
895/* tw_bind_next():
896 *	Begin the list of the shell bindings
897 */
898/*ARGSUSED*/
899int
900tw_bind_next(struct Strbuf *res, struct Strbuf *dir, int *flags)
901{
902    USE(dir);
903    USE(flags);
904    if (tw_bind && tw_bind->name) {
905	const char *ptr;
906
907	for (ptr = tw_bind->name; *ptr != '\0'; ptr++)
908	    Strbuf_append1(res, *ptr);
909	tw_bind++;
910	return 1;
911    }
912    return 0;
913} /* end tw_bind_next */
914
915
916/* tw_limit_start():
917 *	Begin the list of the shell limitings
918 */
919/*ARGSUSED*/
920void
921tw_limit_start(DIR *dfd, const Char *pat)
922{
923    USE(pat);
924    SETDIR(dfd)
925#ifndef HAVENOLIMIT
926    tw_limit = limits;
927#endif /* ! HAVENOLIMIT */
928} /* end tw_limit_start */
929
930
931/* tw_limit_next():
932 *	Begin the list of the shell limitings
933 */
934/*ARGSUSED*/
935int
936tw_limit_next(struct Strbuf *res, struct Strbuf *dir, int *flags)
937{
938    USE(dir);
939    USE(flags);
940#ifndef HAVENOLIMIT
941    if (tw_limit && tw_limit->limname) {
942	const char *ptr;
943
944	for (ptr = tw_limit->limname; *ptr != '\0'; ptr++)
945	    Strbuf_append1(res, *ptr);
946	tw_limit++;
947	return 1;
948    }
949#endif /* ! HAVENOLIMIT */
950    return 0;
951} /* end tw_limit_next */
952
953
954/* tw_sig_start():
955 *	Begin the list of the shell sigings
956 */
957/*ARGSUSED*/
958void
959tw_sig_start(DIR *dfd, const Char *pat)
960{
961    USE(pat);
962    SETDIR(dfd)
963    tw_index = 0;
964} /* end tw_sig_start */
965
966
967/* tw_sig_next():
968 *	Begin the list of the shell sigings
969 */
970/*ARGSUSED*/
971int
972tw_sig_next(struct Strbuf *res, struct Strbuf *dir, int *flags)
973{
974    USE(dir);
975    USE(flags);
976    for (;tw_index < nsig; tw_index++) {
977	const char *ptr;
978
979	if (mesg[tw_index].iname == NULL)
980	    continue;
981
982	for (ptr = mesg[tw_index].iname; *ptr != '\0'; ptr++)
983	    Strbuf_append1(res, *ptr);
984	tw_index++;
985	return 1;
986    }
987    return 0;
988} /* end tw_sig_next */
989
990
991/* tw_job_start():
992 *	Begin the list of the shell jobings
993 */
994/*ARGSUSED*/
995void
996tw_job_start(DIR *dfd, const Char *pat)
997{
998    USE(pat);
999    SETDIR(dfd)
1000    tw_index = 1;
1001} /* end tw_job_start */
1002
1003
1004/* tw_job_next():
1005 *	Begin the list of the shell jobings
1006 */
1007/*ARGSUSED*/
1008int
1009tw_job_next(struct Strbuf *res, struct Strbuf *dir, int *flags)
1010{
1011    struct process *j;
1012
1013    USE(dir);
1014    USE(flags);
1015    for (;tw_index <= pmaxindex; tw_index++) {
1016	for (j = proclist.p_next; j != NULL; j = j->p_next)
1017	    if (j->p_index == tw_index && j->p_procid == j->p_jobid)
1018		break;
1019	if (j == NULL)
1020	    continue;
1021	Strbuf_append(res, j->p_command);
1022	tw_index++;
1023	return 1;
1024    }
1025    return 0;
1026} /* end tw_job_next */
1027