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