tw.init.c revision 167466
1/* $Header: /p/tcsh/cvsroot/tcsh/tw.init.c,v 3.39 2006/03/02 18:46:45 christos Exp $ */
2/*
3 * tw.init.c: Handle lists of things to complete
4 */
5/*-
6 * Copyright (c) 1980, 1991 The Regents of the University of California.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33#include "sh.h"
34
35RCSID("$tcsh: tw.init.c,v 3.39 2006/03/02 18:46:45 christos Exp $")
36
37#include "tw.h"
38#include "ed.h"
39#include "tc.h"
40#include "sh.proc.h"
41
42#define TW_INCR	128
43
44typedef struct {
45    Char **list, 			/* List of command names	*/
46	  *buff;			/* Space holding command names	*/
47    size_t nlist, 			/* Number of items		*/
48           nbuff,			/* Current space in name buf	*/
49           tlist,			/* Total space in list		*/
50	   tbuff;			/* Total space in name buf	*/
51} stringlist_t;
52
53
54static struct varent *tw_vptr = NULL;	/* Current shell variable 	*/
55static Char **tw_env = NULL;		/* Current environment variable */
56static const Char *tw_word;		/* Current word pointer		*/
57static struct KeyFuncs *tw_bind = NULL;	/* List of the bindings		*/
58#ifndef HAVENOLIMIT
59static struct limits *tw_limit = NULL;	/* List of the resource limits	*/
60#endif /* HAVENOLIMIT */
61static int tw_index = 0;		/* signal and job index		*/
62static DIR   *tw_dir_fd = NULL;		/* Current directory descriptor	*/
63static int    tw_cmd_got = 0;		/* What we need to do		*/
64static stringlist_t tw_cmd  = { NULL, NULL, 0, 0, 0, 0 };
65static stringlist_t tw_item = { NULL, NULL, 0, 0, 0, 0 };
66#define TW_FL_CMD	0x01
67#define TW_FL_ALIAS	0x02
68#define TW_FL_BUILTIN	0x04
69#define TW_FL_SORT	0x08
70#define TW_FL_REL	0x10
71
72static struct {				/* Current element pointer	*/
73    size_t cur;				/* Current element number	*/
74    Char **pathv;			/* Current element in path	*/
75    DIR   *dfd;				/* Current directory descriptor	*/
76} tw_cmd_state;
77
78
79#define SETDIR(dfd) \
80    { \
81	tw_dir_fd = dfd; \
82	if (tw_dir_fd != NULL) \
83	    rewinddir(tw_dir_fd); \
84    }
85
86#define CLRDIR(dfd) \
87    if (dfd != NULL) { \
88	pintr_disabled++; \
89	xclosedir(dfd); \
90	dfd = NULL; \
91	disabled_cleanup(&pintr_disabled); \
92    }
93
94static Char	*tw_str_add		(stringlist_t *, size_t);
95static void	 tw_str_free		(stringlist_t *);
96static int       tw_dir_next		(struct Strbuf *, DIR *);
97static void	 tw_cmd_add 		(const Char *name);
98static void 	 tw_cmd_cmd		(void);
99static void	 tw_cmd_builtin		(void);
100static void	 tw_cmd_alias		(void);
101static void	 tw_cmd_sort		(void);
102static void 	 tw_vptr_start		(struct varent *);
103
104
105/* tw_str_add():
106 *	Add an item to the string list
107 */
108static Char *
109tw_str_add(stringlist_t *sl, size_t len)
110{
111    Char *ptr;
112
113    if (sl->tlist <= sl->nlist) {
114	pintr_disabled++;
115	sl->tlist += TW_INCR;
116	sl->list = xrealloc(sl->list, sl->tlist * sizeof(Char *));
117	disabled_cleanup(&pintr_disabled);
118    }
119    if (sl->tbuff <= sl->nbuff + len) {
120	size_t i;
121
122	ptr = sl->buff;
123	pintr_disabled++;
124	sl->tbuff += TW_INCR + len;
125	sl->buff = xrealloc(sl->buff, sl->tbuff * sizeof(Char));
126	/* Re-thread the new pointer list, if changed */
127	if (ptr != NULL && ptr != sl->buff) {
128	    intptr_t offs = sl->buff - ptr;
129	    for (i = 0; i < sl->nlist; i++)
130		sl->list[i] += offs;
131	}
132	disabled_cleanup(&pintr_disabled);
133    }
134    ptr = sl->list[sl->nlist++] = &sl->buff[sl->nbuff];
135    sl->nbuff += len;
136    return ptr;
137} /* tw_str_add */
138
139
140/* tw_str_free():
141 *	Free a stringlist
142 */
143static void
144tw_str_free(stringlist_t *sl)
145{
146    pintr_disabled++;
147    if (sl->list) {
148	xfree(sl->list);
149	sl->list = NULL;
150	sl->tlist = sl->nlist = 0;
151    }
152    if (sl->buff) {
153	xfree(sl->buff);
154	sl->buff = NULL;
155	sl->tbuff = sl->nbuff = 0;
156    }
157    disabled_cleanup(&pintr_disabled);
158} /* end tw_str_free */
159
160
161static int
162tw_dir_next(struct Strbuf *res, DIR *dfd)
163{
164    struct dirent *dirp;
165
166    if (dfd == NULL)
167	return 0;
168
169    if ((dirp = readdir(dfd)) != NULL) {
170	Strbuf_append(res, str2short(dirp->d_name));
171	return 1;
172    }
173    return 0;
174} /* end tw_dir_next */
175
176
177/* tw_cmd_add():
178 *	Add the name to the command list
179 */
180static void
181tw_cmd_add(const Char *name)
182{
183    size_t len;
184
185    len = Strlen(name) + 2;
186    (void) Strcpy(tw_str_add(&tw_cmd, len), name);
187} /* end tw_cmd_add */
188
189
190/* tw_cmd_free():
191 *	Free the command list
192 */
193void
194tw_cmd_free(void)
195{
196    CLRDIR(tw_dir_fd)
197    tw_str_free(&tw_cmd);
198    tw_cmd_got = 0;
199} /* end tw_cmd_free */
200
201/* tw_cmd_cmd():
202 *	Add system commands to the command list
203 */
204static void
205tw_cmd_cmd(void)
206{
207    DIR *dirp;
208    struct dirent *dp;
209    Char *dir = NULL, *name;
210    Char **pv;
211    struct varent *v = adrof(STRpath);
212    struct varent *recexec = adrof(STRrecognize_only_executables);
213    size_t len;
214
215
216    if (v == NULL || v->vec == NULL) /* if no path */
217	return;
218
219    for (pv = v->vec; *pv; pv++) {
220	if (pv[0][0] != '/') {
221	    tw_cmd_got |= TW_FL_REL;
222	    continue;
223	}
224
225	if ((dirp = opendir(short2str(*pv))) == NULL)
226	    continue;
227
228	cleanup_push(dirp, opendir_cleanup);
229	if (recexec) {
230	    dir = Strspl(*pv, STRslash);
231	    cleanup_push(dir, xfree);
232	}
233	while ((dp = readdir(dirp)) != NULL) {
234#if defined(_UWIN) || defined(__CYGWIN__)
235	    /* Turn foo.{exe,com,bat} into foo since UWIN's readdir returns
236	     * the file with the .exe, .com, .bat extension
237	     */
238	    len = strlen(dp->d_name);
239	    if (len > 4 && (strcmp(&dp->d_name[len - 4], ".exe") == 0 ||
240		strcmp(&dp->d_name[len - 4], ".bat") == 0 ||
241		strcmp(&dp->d_name[len - 4], ".com") == 0))
242		dp->d_name[len - 4] = '\0';
243#endif /* _UWIN || __CYGWIN__ */
244	    /* the call to executable() may make this a bit slow */
245	    name = str2short(dp->d_name);
246	    if (dp->d_ino == 0 || (recexec && !executable(dir, name, 0)))
247		continue;
248            len = Strlen(name);
249            if (name[0] == '#' ||	/* emacs temp files	*/
250		name[0] == '.' ||	/* .files		*/
251		name[len - 1] == '~' ||	/* emacs backups	*/
252		name[len - 1] == '%')	/* textedit backups	*/
253                continue;		/* Ignore!		*/
254            tw_cmd_add(name);
255	}
256	cleanup_until(dirp);
257    }
258} /* end tw_cmd_cmd */
259
260
261/* tw_cmd_builtin():
262 *	Add builtins to the command list
263 */
264static void
265tw_cmd_builtin(void)
266{
267    const struct biltins *bptr;
268
269    for (bptr = bfunc; bptr < &bfunc[nbfunc]; bptr++)
270	if (bptr->bname)
271	    tw_cmd_add(str2short(bptr->bname));
272#ifdef WINNT_NATIVE
273    for (bptr = nt_bfunc; bptr < &nt_bfunc[nt_nbfunc]; bptr++)
274	if (bptr->bname)
275	    tw_cmd_add(str2short(bptr->bname));
276#endif /* WINNT_NATIVE*/
277} /* end tw_cmd_builtin */
278
279
280/* tw_cmd_alias():
281 *	Add aliases to the command list
282 */
283static void
284tw_cmd_alias(void)
285{
286    struct varent *p;
287    struct varent *c;
288
289    p = &aliases;
290    for (;;) {
291	while (p->v_left)
292	    p = p->v_left;
293x:
294	if (p->v_parent == 0) /* is it the header? */
295	    return;
296	if (p->v_name)
297	    tw_cmd_add(p->v_name);
298	if (p->v_right) {
299	    p = p->v_right;
300	    continue;
301	}
302	do {
303	    c = p;
304	    p = p->v_parent;
305	} while (p->v_right == c);
306	goto x;
307    }
308} /* end tw_cmd_alias */
309
310
311/* tw_cmd_sort():
312 *	Sort the command list removing duplicate elements
313 */
314static void
315tw_cmd_sort(void)
316{
317    size_t fwd, i;
318
319    pintr_disabled++;
320    /* sort the list. */
321    qsort(tw_cmd.list, tw_cmd.nlist, sizeof(Char *), fcompare);
322
323    /* get rid of multiple entries */
324    for (i = 0, fwd = 0; i + 1 < tw_cmd.nlist; i++) {
325	if (Strcmp(tw_cmd.list[i], tw_cmd.list[i + 1]) == 0) /* garbage */
326	    fwd++;		/* increase the forward ref. count */
327	else if (fwd)
328	    tw_cmd.list[i - fwd] = tw_cmd.list[i];
329    }
330    /* Fix fencepost error -- Theodore Ts'o <tytso@athena.mit.edu> */
331    if (fwd)
332	tw_cmd.list[i - fwd] = tw_cmd.list[i];
333    tw_cmd.nlist -= fwd;
334    disabled_cleanup(&pintr_disabled);
335} /* end tw_cmd_sort */
336
337
338/* tw_cmd_start():
339 *	Get the command list and sort it, if not done yet.
340 *	Reset the current pointer to the beginning of the command list
341 */
342/*ARGSUSED*/
343void
344tw_cmd_start(DIR *dfd, const Char *pat)
345{
346    static Char *defpath[] = { STRNULL, 0 };
347    USE(pat);
348    SETDIR(dfd)
349    if ((tw_cmd_got & TW_FL_CMD) == 0) {
350	tw_cmd_free();
351	tw_cmd_cmd();
352	tw_cmd_got |= TW_FL_CMD;
353    }
354    if ((tw_cmd_got & TW_FL_ALIAS) == 0) {
355	tw_cmd_alias();
356	tw_cmd_got &= ~TW_FL_SORT;
357	tw_cmd_got |= TW_FL_ALIAS;
358    }
359    if ((tw_cmd_got & TW_FL_BUILTIN) == 0) {
360	tw_cmd_builtin();
361	tw_cmd_got &= ~TW_FL_SORT;
362	tw_cmd_got |= TW_FL_BUILTIN;
363    }
364    if ((tw_cmd_got & TW_FL_SORT) == 0) {
365	tw_cmd_sort();
366	tw_cmd_got |= TW_FL_SORT;
367    }
368
369    tw_cmd_state.cur = 0;
370    CLRDIR(tw_cmd_state.dfd)
371    if (tw_cmd_got & TW_FL_REL) {
372	struct varent *vp = adrof(STRpath);
373	if (vp && vp->vec)
374	    tw_cmd_state.pathv = vp->vec;
375	else
376	    tw_cmd_state.pathv = defpath;
377    }
378    else
379	tw_cmd_state.pathv = defpath;
380} /* tw_cmd_start */
381
382
383/* tw_cmd_next():
384 *	Return the next element in the command list or
385 *	Look for commands in the relative path components
386 */
387int
388tw_cmd_next(struct Strbuf *res, struct Strbuf *dir, int *flags)
389{
390    int ret = 0;
391    Char *ptr;
392
393    if (tw_cmd_state.cur < tw_cmd.nlist) {
394	*flags = TW_DIR_OK;
395	Strbuf_append(res, tw_cmd.list[tw_cmd_state.cur++]);
396	return 1;
397    }
398
399    /*
400     * We need to process relatives in the path.
401     */
402    while ((tw_cmd_state.dfd == NULL ||
403	    (ret = tw_dir_next(res, tw_cmd_state.dfd)) == 0) &&
404	   *tw_cmd_state.pathv != NULL) {
405
406        CLRDIR(tw_cmd_state.dfd)
407
408	while (*tw_cmd_state.pathv && tw_cmd_state.pathv[0][0] == '/')
409	    tw_cmd_state.pathv++;
410	if ((ptr = *tw_cmd_state.pathv) != 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)
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)
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)
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