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