tw.init.c 22.3 KB
Newer Older
1
/* $Header: /p/tcsh/cvsroot/tcsh/tw.init.c,v 3.42 2011/04/17 14:49:30 christos Exp $ */
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
/*
 * tw.init.c: Handle lists of things to complete
 */
/*-
 * Copyright (c) 1980, 1991 The Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
#include "sh.h"

35
RCSID("$tcsh: tw.init.c,v 3.42 2011/04/17 14:49:30 christos Exp $")
36 37 38 39 40 41 42 43 44 45 46

#include "tw.h"
#include "ed.h"
#include "tc.h"
#include "sh.proc.h"

#define TW_INCR	128

typedef struct {
    Char **list, 			/* List of command names	*/
	  *buff;			/* Space holding command names	*/
47
    size_t nlist, 			/* Number of items		*/
48 49 50 51 52 53 54 55
           nbuff,			/* Current space in name buf	*/
           tlist,			/* Total space in list		*/
	   tbuff;			/* Total space in name buf	*/
} stringlist_t;


static struct varent *tw_vptr = NULL;	/* Current shell variable 	*/
static Char **tw_env = NULL;		/* Current environment variable */
56
static const Char *tw_word;		/* Current word pointer		*/
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
static struct KeyFuncs *tw_bind = NULL;	/* List of the bindings		*/
#ifndef HAVENOLIMIT
static struct limits *tw_limit = NULL;	/* List of the resource limits	*/
#endif /* HAVENOLIMIT */
static int tw_index = 0;		/* signal and job index		*/
static DIR   *tw_dir_fd = NULL;		/* Current directory descriptor	*/
static int    tw_cmd_got = 0;		/* What we need to do		*/
static stringlist_t tw_cmd  = { NULL, NULL, 0, 0, 0, 0 };
static stringlist_t tw_item = { NULL, NULL, 0, 0, 0, 0 };
#define TW_FL_CMD	0x01
#define TW_FL_ALIAS	0x02
#define TW_FL_BUILTIN	0x04
#define TW_FL_SORT	0x08
#define TW_FL_REL	0x10

static struct {				/* Current element pointer	*/
73
    size_t cur;				/* Current element number	*/
74 75 76 77 78 79 80 81 82 83 84 85 86 87
    Char **pathv;			/* Current element in path	*/
    DIR   *dfd;				/* Current directory descriptor	*/
} tw_cmd_state;


#define SETDIR(dfd) \
    { \
	tw_dir_fd = dfd; \
	if (tw_dir_fd != NULL) \
	    rewinddir(tw_dir_fd); \
    }

#define CLRDIR(dfd) \
    if (dfd != NULL) { \
88 89
	pintr_disabled++; \
	xclosedir(dfd); \
90
	dfd = NULL; \
91
	disabled_cleanup(&pintr_disabled); \
92 93
    }

94 95 96 97 98 99 100 101 102
static Char	*tw_str_add		(stringlist_t *, size_t);
static void	 tw_str_free		(stringlist_t *);
static int       tw_dir_next		(struct Strbuf *, DIR *);
static void	 tw_cmd_add 		(const Char *name);
static void 	 tw_cmd_cmd		(void);
static void	 tw_cmd_builtin		(void);
static void	 tw_cmd_alias		(void);
static void	 tw_cmd_sort		(void);
static void 	 tw_vptr_start		(struct varent *);
103 104 105 106 107 108


/* tw_str_add():
 *	Add an item to the string list
 */
static Char *
109
tw_str_add(stringlist_t *sl, size_t len)
110 111 112 113
{
    Char *ptr;

    if (sl->tlist <= sl->nlist) {
114
	pintr_disabled++;
115
	sl->tlist += TW_INCR;
116 117
	sl->list = xrealloc(sl->list, sl->tlist * sizeof(Char *));
	disabled_cleanup(&pintr_disabled);
118 119
    }
    if (sl->tbuff <= sl->nbuff + len) {
120
	size_t i;
121

122 123
	ptr = sl->buff;
	pintr_disabled++;
124
	sl->tbuff += TW_INCR + len;
125
	sl->buff = xrealloc(sl->buff, sl->tbuff * sizeof(Char));
126 127 128 129 130 131
	/* Re-thread the new pointer list, if changed */
	if (ptr != NULL && ptr != sl->buff) {
	    intptr_t offs = sl->buff - ptr;
	    for (i = 0; i < sl->nlist; i++)
		sl->list[i] += offs;
	}
132
	disabled_cleanup(&pintr_disabled);
133 134 135 136 137 138 139 140 141 142 143
    }
    ptr = sl->list[sl->nlist++] = &sl->buff[sl->nbuff];
    sl->nbuff += len;
    return ptr;
} /* tw_str_add */


/* tw_str_free():
 *	Free a stringlist
 */
static void
144
tw_str_free(stringlist_t *sl)
145
{
146
    pintr_disabled++;
147
    if (sl->list) {
148
	xfree(sl->list);
149 150 151 152
	sl->list = NULL;
	sl->tlist = sl->nlist = 0;
    }
    if (sl->buff) {
153
	xfree(sl->buff);
154 155 156
	sl->buff = NULL;
	sl->tbuff = sl->nbuff = 0;
    }
157
    disabled_cleanup(&pintr_disabled);
158 159 160
} /* end tw_str_free */


161 162
static int
tw_dir_next(struct Strbuf *res, DIR *dfd)
163 164 165 166
{
    struct dirent *dirp;

    if (dfd == NULL)
167
	return 0;
168 169

    if ((dirp = readdir(dfd)) != NULL) {
170 171
	Strbuf_append(res, str2short(dirp->d_name));
	return 1;
172
    }
173
    return 0;
174 175 176 177 178 179 180
} /* end tw_dir_next */


/* tw_cmd_add():
 *	Add the name to the command list
 */
static void
181
tw_cmd_add(const Char *name)
182
{
183
    size_t len;
184

185
    len = Strlen(name) + 2;
186 187 188 189 190 191 192 193
    (void) Strcpy(tw_str_add(&tw_cmd, len), name);
} /* end tw_cmd_add */


/* tw_cmd_free():
 *	Free the command list
 */
void
194
tw_cmd_free(void)
195 196 197 198 199 200 201 202 203 204
{
    CLRDIR(tw_dir_fd)
    tw_str_free(&tw_cmd);
    tw_cmd_got = 0;
} /* end tw_cmd_free */

/* tw_cmd_cmd():
 *	Add system commands to the command list
 */
static void
205
tw_cmd_cmd(void)
206 207 208 209 210 211 212
{
    DIR *dirp;
    struct dirent *dp;
    Char *dir = NULL, *name;
    Char **pv;
    struct varent *v = adrof(STRpath);
    struct varent *recexec = adrof(STRrecognize_only_executables);
213
    size_t len;
214 215 216 217 218 219 220 221 222 223 224 225 226 227


    if (v == NULL || v->vec == NULL) /* if no path */
	return;

    for (pv = v->vec; *pv; pv++) {
	if (pv[0][0] != '/') {
	    tw_cmd_got |= TW_FL_REL;
	    continue;
	}

	if ((dirp = opendir(short2str(*pv))) == NULL)
	    continue;

228 229
	cleanup_push(dirp, opendir_cleanup);
	if (recexec) {
230
	    dir = Strspl(*pv, STRslash);
231 232
	    cleanup_push(dir, xfree);
	}
233 234 235 236
	while ((dp = readdir(dirp)) != NULL) {
#if defined(_UWIN) || defined(__CYGWIN__)
	    /* Turn foo.{exe,com,bat} into foo since UWIN's readdir returns
	     * the file with the .exe, .com, .bat extension
237 238
	     *
	     * Same for Cygwin, but only for .exe and .com extension.
239
	     */
240 241
	    len = strlen(dp->d_name);
	    if (len > 4 && (strcmp(&dp->d_name[len - 4], ".exe") == 0 ||
242
#ifndef __CYGWIN__
243
		strcmp(&dp->d_name[len - 4], ".bat") == 0 ||
244
#endif /* !__CYGWIN__ */
245 246
		strcmp(&dp->d_name[len - 4], ".com") == 0))
		dp->d_name[len - 4] = '\0';
247 248 249 250 251
#endif /* _UWIN || __CYGWIN__ */
	    /* the call to executable() may make this a bit slow */
	    name = str2short(dp->d_name);
	    if (dp->d_ino == 0 || (recexec && !executable(dir, name, 0)))
		continue;
252
            len = Strlen(name);
253 254
            if (name[0] == '#' ||	/* emacs temp files	*/
		name[0] == '.' ||	/* .files		*/
255 256
		name[len - 1] == '~' ||	/* emacs backups	*/
		name[len - 1] == '%')	/* textedit backups	*/
257 258 259
                continue;		/* Ignore!		*/
            tw_cmd_add(name);
	}
260
	cleanup_until(dirp);
261 262 263 264 265 266 267 268
    }
} /* end tw_cmd_cmd */


/* tw_cmd_builtin():
 *	Add builtins to the command list
 */
static void
269
tw_cmd_builtin(void)
270
{
271
    const struct biltins *bptr;
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287

    for (bptr = bfunc; bptr < &bfunc[nbfunc]; bptr++)
	if (bptr->bname)
	    tw_cmd_add(str2short(bptr->bname));
#ifdef WINNT_NATIVE
    for (bptr = nt_bfunc; bptr < &nt_bfunc[nt_nbfunc]; bptr++)
	if (bptr->bname)
	    tw_cmd_add(str2short(bptr->bname));
#endif /* WINNT_NATIVE*/
} /* end tw_cmd_builtin */


/* tw_cmd_alias():
 *	Add aliases to the command list
 */
static void
288
tw_cmd_alias(void)
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
{
    struct varent *p;
    struct varent *c;

    p = &aliases;
    for (;;) {
	while (p->v_left)
	    p = p->v_left;
x:
	if (p->v_parent == 0) /* is it the header? */
	    return;
	if (p->v_name)
	    tw_cmd_add(p->v_name);
	if (p->v_right) {
	    p = p->v_right;
	    continue;
	}
	do {
	    c = p;
	    p = p->v_parent;
	} while (p->v_right == c);
	goto x;
    }
} /* end tw_cmd_alias */


/* tw_cmd_sort():
 *	Sort the command list removing duplicate elements
 */
static void
319
tw_cmd_sort(void)
320
{
321
    size_t fwd, i;
322

323
    pintr_disabled++;
324
    /* sort the list. */
325
    qsort(tw_cmd.list, tw_cmd.nlist, sizeof(Char *), fcompare);
326 327

    /* get rid of multiple entries */
328
    for (i = 0, fwd = 0; i + 1 < tw_cmd.nlist; i++) {
329 330 331 332 333 334 335 336 337
	if (Strcmp(tw_cmd.list[i], tw_cmd.list[i + 1]) == 0) /* garbage */
	    fwd++;		/* increase the forward ref. count */
	else if (fwd) 
	    tw_cmd.list[i - fwd] = tw_cmd.list[i];
    }
    /* Fix fencepost error -- Theodore Ts'o <tytso@athena.mit.edu> */
    if (fwd)
	tw_cmd.list[i - fwd] = tw_cmd.list[i];
    tw_cmd.nlist -= fwd;
338
    disabled_cleanup(&pintr_disabled);
339 340 341 342 343 344 345 346 347
} /* end tw_cmd_sort */


/* tw_cmd_start():
 *	Get the command list and sort it, if not done yet.
 *	Reset the current pointer to the beginning of the command list
 */
/*ARGSUSED*/
void
348
tw_cmd_start(DIR *dfd, const Char *pat)
349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390
{
    static Char *defpath[] = { STRNULL, 0 };
    USE(pat);
    SETDIR(dfd)
    if ((tw_cmd_got & TW_FL_CMD) == 0) {
	tw_cmd_free();
	tw_cmd_cmd();
	tw_cmd_got |= TW_FL_CMD;
    }
    if ((tw_cmd_got & TW_FL_ALIAS) == 0) {
	tw_cmd_alias();
	tw_cmd_got &= ~TW_FL_SORT;
	tw_cmd_got |= TW_FL_ALIAS;
    }
    if ((tw_cmd_got & TW_FL_BUILTIN) == 0) {
	tw_cmd_builtin();
	tw_cmd_got &= ~TW_FL_SORT;
	tw_cmd_got |= TW_FL_BUILTIN;
    }
    if ((tw_cmd_got & TW_FL_SORT) == 0) {
	tw_cmd_sort();
	tw_cmd_got |= TW_FL_SORT;
    }

    tw_cmd_state.cur = 0;
    CLRDIR(tw_cmd_state.dfd)
    if (tw_cmd_got & TW_FL_REL) {
	struct varent *vp = adrof(STRpath);
	if (vp && vp->vec)
	    tw_cmd_state.pathv = vp->vec;
	else
	    tw_cmd_state.pathv = defpath;
    }
    else 
	tw_cmd_state.pathv = defpath;
} /* tw_cmd_start */


/* tw_cmd_next():
 *	Return the next element in the command list or
 *	Look for commands in the relative path components
 */
391 392
int
tw_cmd_next(struct Strbuf *res, struct Strbuf *dir, int *flags)
393
{
394 395
    int ret = 0;
    Char *ptr;
396 397 398

    if (tw_cmd_state.cur < tw_cmd.nlist) {
	*flags = TW_DIR_OK;
399 400
	Strbuf_append(res, tw_cmd.list[tw_cmd_state.cur++]);
	return 1;
401 402 403 404 405
    }

    /*
     * We need to process relatives in the path.
     */
406
    while ((tw_cmd_state.dfd == NULL ||
407
	    (res->len = 0, ret = tw_dir_next(res, tw_cmd_state.dfd)) == 0) &&
408
	   *tw_cmd_state.pathv != NULL) {
409 410 411 412 413 414

        CLRDIR(tw_cmd_state.dfd)

	while (*tw_cmd_state.pathv && tw_cmd_state.pathv[0][0] == '/')
	    tw_cmd_state.pathv++;
	if ((ptr = *tw_cmd_state.pathv) != 0) {
415
	    res->len = 0;
416 417
	    Strbuf_append(res, ptr);
	    ret = 1;
418 419 420 421
	    /*
	     * We complete directories only on '.' should that
	     * be changed?
	     */
422
	    dir->len = 0;
423 424
	    if (ptr[0] == '\0' || (ptr[0] == '.' && ptr[1] == '\0')) {
		tw_cmd_state.dfd = opendir(".");
425
		*flags = TW_DIR_OK | TW_EXEC_CHK;
426 427
	    }
	    else {
428 429
		Strbuf_append(dir, *tw_cmd_state.pathv);
		Strbuf_append1(dir, '/');
430 431 432
		tw_cmd_state.dfd = opendir(short2str(*tw_cmd_state.pathv));
		*flags = TW_EXEC_CHK;
	    }
433
	    Strbuf_terminate(dir);
434 435 436
	    tw_cmd_state.pathv++;
	}
    }
437
    return ret;
438 439 440 441 442 443 444
} /* end tw_cmd_next */


/* tw_vptr_start():
 *	Find the first variable in the variable list
 */
static void
445
tw_vptr_start(struct varent *c)
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475
{
    tw_vptr = c;		/* start at beginning of variable list */

    for (;;) {
	while (tw_vptr->v_left)
	    tw_vptr = tw_vptr->v_left;
x:
	if (tw_vptr->v_parent == 0) {	/* is it the header? */
	    tw_vptr = NULL;
	    return;
	}
	if (tw_vptr->v_name)
	    return;		/* found first one */
	if (tw_vptr->v_right) {
	    tw_vptr = tw_vptr->v_right;
	    continue;
	}
	do {
	    c = tw_vptr;
	    tw_vptr = tw_vptr->v_parent;
	} while (tw_vptr->v_right == c);
	goto x;
    }
} /* end tw_shvar_start */


/* tw_shvar_next():
 *	Return the next shell variable
 */
/*ARGSUSED*/
476 477
int
tw_shvar_next(struct Strbuf *res, struct Strbuf *dir, int *flags)
478 479 480 481 482 483 484
{
    struct varent *p;
    struct varent *c;

    USE(flags);
    USE(dir);
    if ((p = tw_vptr) == NULL)
485
	return 0;		/* just in case */
486

487
    Strbuf_append(res, p->v_name); /* we know that this name is here now */
488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503

    /* now find the next one */
    for (;;) {
	if (p->v_right) {	/* if we can go right */
	    p = p->v_right;
	    while (p->v_left)
		p = p->v_left;
	}
	else {			/* else go up */
	    do {
		c = p;
		p = p->v_parent;
	    } while (p->v_right == c);
	}
	if (p->v_parent == 0) {	/* is it the header? */
	    tw_vptr = NULL;
504
	    return 1;
505 506 507
	}
	if (p->v_name) {
	    tw_vptr = p;	/* save state for the next call */
508
	    return 1;
509 510 511 512 513 514 515 516 517
	}
    }
} /* end tw_shvar_next */


/* tw_envvar_next():
 *	Return the next environment variable
 */
/*ARGSUSED*/
518 519
int
tw_envvar_next(struct Strbuf *res, struct Strbuf *dir, int *flags)
520
{
521
    const Char *ps;
522 523 524 525

    USE(flags);
    USE(dir);
    if (tw_env == NULL || *tw_env == NULL)
526 527
	return 0;
    for (ps = *tw_env; *ps && *ps != '='; ps++)
528
	continue;
529
    Strbuf_appendn(res, *tw_env, ps - *tw_env);
530
    tw_env++;
531
    return 1;
532 533 534 535 536 537 538 539
} /* end tw_envvar_next */


/* tw_var_start():
 *	Begin the list of the shell and environment variables
 */
/*ARGSUSED*/
void
540
tw_var_start(DIR *dfd, const Char *pat)
541 542 543 544 545 546 547 548 549 550 551 552 553
{
    USE(pat);
    SETDIR(dfd)
    tw_vptr_start(&shvhed);
    tw_env = STR_environ;
} /* end tw_var_start */


/* tw_alias_start():
 *	Begin the list of the shell aliases
 */
/*ARGSUSED*/
void
554
tw_alias_start(DIR *dfd, const Char *pat)
555 556 557 558 559 560 561 562 563 564 565 566 567
{
    USE(pat);
    SETDIR(dfd)
    tw_vptr_start(&aliases);
    tw_env = NULL;
} /* tw_alias_start */


/* tw_complete_start():
 *	Begin the list of completions
 */
/*ARGSUSED*/
void
568
tw_complete_start(DIR *dfd, const Char *pat)
569 570 571 572 573 574 575 576 577 578 579
{
    USE(pat);
    SETDIR(dfd)
    tw_vptr_start(&completions);
    tw_env = NULL;
} /* end tw_complete_start */


/* tw_var_next():
 *	Return the next shell or environment variable
 */
580 581
int
tw_var_next(struct Strbuf *res, struct Strbuf *dir, int *flags)
582
{
583
    int ret = 0;
584 585

    if (tw_vptr)
586 587 588 589
	ret = tw_shvar_next(res, dir, flags);
    if (ret == 0 && tw_env)
	ret = tw_envvar_next(res, dir, flags);
    return ret;
590 591 592 593 594 595 596 597
} /* end tw_var_next */


/* tw_logname_start():
 *	Initialize lognames to the beginning of the list
 */
/*ARGSUSED*/
void 
598
tw_logname_start(DIR *dfd, const Char *pat)
599 600 601 602 603 604 605 606 607 608 609 610 611
{
    USE(pat);
    SETDIR(dfd)
#ifdef HAVE_GETPWENT
    (void) setpwent();	/* Open passwd file */
#endif
} /* end tw_logname_start */


/* tw_logname_next():
 *	Return the next entry from the passwd file
 */
/*ARGSUSED*/
612 613
int
tw_logname_next(struct Strbuf *res, struct Strbuf *dir, int *flags)
614 615
{
    struct passwd *pw;
616

617 618 619 620 621 622 623 624
    /*
     * We don't want to get interrupted inside getpwent()
     * because the yellow pages code is not interruptible,
     * and if we call endpwent() immediatetely after
     * (in pintr()) we may be freeing an invalid pointer
     */
    USE(flags);
    USE(dir);
625
    pintr_disabled++;
626 627 628 629 630
#ifdef HAVE_GETPWENT
    pw = getpwent();
#else
    pw = NULL;
#endif
631
    disabled_cleanup(&pintr_disabled);
632 633 634 635 636

    if (pw == NULL) {
#ifdef YPBUGS
	fix_yp_bugs();
#endif
637
	return 0;
638
    }
639 640
    Strbuf_append(res, str2short(pw->pw_name));
    return 1;
641 642 643 644 645 646 647
} /* end tw_logname_next */


/* tw_logname_end():
 *	Close the passwd file to finish the logname list
 */
void
648
tw_logname_end(void)
649 650 651 652 653 654 655 656 657 658 659 660 661 662 663
{
#ifdef YPBUGS
    fix_yp_bugs();
#endif
#ifdef HAVE_GETPWENT
   (void) endpwent();
#endif
} /* end tw_logname_end */


/* tw_grpname_start():
 *	Initialize grpnames to the beginning of the list
 */
/*ARGSUSED*/
void 
664
tw_grpname_start(DIR *dfd, const Char *pat)
665 666 667
{
    USE(pat);
    SETDIR(dfd)
668
#if !defined(_VMS_POSIX) && !defined(_OSD_POSIX) && !defined(WINNT_NATIVE) && !defined (__ANDROID__)
669 670 671 672 673 674 675 676 677
    (void) setgrent();	/* Open group file */
#endif /* !_VMS_POSIX && !_OSD_POSIX && !WINNT_NATIVE */
} /* end tw_grpname_start */


/* tw_grpname_next():
 *	Return the next entry from the group file
 */
/*ARGSUSED*/
678 679
int
tw_grpname_next(struct Strbuf *res, struct Strbuf *dir, int *flags)
680 681
{
    struct group *gr;
682

683 684 685 686 687 688 689 690
    /*
     * We don't want to get interrupted inside getgrent()
     * because the yellow pages code is not interruptible,
     * and if we call endgrent() immediatetely after
     * (in pintr()) we may be freeing an invalid pointer
     */
    USE(flags);
    USE(dir);
691
    pintr_disabled++;
692
#if !defined(_VMS_POSIX) && !defined(_OSD_POSIX) && !defined(WINNT_NATIVE) && !defined(__ANDROID__)
693 694 695 696 697
    errno = 0;
    while ((gr = getgrent()) == NULL && errno == EINTR) {
	handle_pending_signals();
	errno = 0;
    }
698 699 700
#else /* _VMS_POSIX || _OSD_POSIX || WINNT_NATIVE */
    gr = NULL;
#endif /* !_VMS_POSIX && !_OSD_POSIX && !WINNT_NATIVE */
701
    disabled_cleanup(&pintr_disabled);
702 703 704 705 706

    if (gr == NULL) {
#ifdef YPBUGS
	fix_yp_bugs();
#endif
707
	return 0;
708
    }
709 710
    Strbuf_append(res, str2short(gr->gr_name));
    return 1;
711 712 713 714 715 716 717
} /* end tw_grpname_next */


/* tw_grpname_end():
 *	Close the group file to finish the groupname list
 */
void
718
tw_grpname_end(void)
719 720 721 722
{
#ifdef YPBUGS
    fix_yp_bugs();
#endif
723
#if !defined(_VMS_POSIX) && !defined(_OSD_POSIX) && !defined(WINNT_NATIVE) && !defined (__ANDROID__)
724 725 726 727 728 729 730 731 732
   (void) endgrent();
#endif /* !_VMS_POSIX && !_OSD_POSIX && !WINNT_NATIVE */
} /* end tw_grpname_end */

/* tw_file_start():
 *	Initialize the directory for the file list
 */
/*ARGSUSED*/
void
733
tw_file_start(DIR *dfd, const Char *pat)
734 735 736 737 738 739 740 741 742 743 744 745
{
    struct varent *vp;
    USE(pat);
    SETDIR(dfd)
    if ((vp = adrof(STRcdpath)) != NULL)
	tw_env = vp->vec;
} /* end tw_file_start */


/* tw_file_next():
 *	Return the next file in the directory 
 */
746 747
int
tw_file_next(struct Strbuf *res, struct Strbuf *dir, int *flags)
748
{
749 750
    int ret = tw_dir_next(res, tw_dir_fd);
    if (ret == 0 && (*flags & TW_DIR_OK) != 0) {
751 752 753 754 755 756
	CLRDIR(tw_dir_fd)
	while (tw_env && *tw_env)
	    if ((tw_dir_fd = opendir(short2str(*tw_env))) != NULL)
		break;
	    else
		tw_env++;
757

758
	if (tw_dir_fd) {
759 760 761 762 763
	    dir->len = 0;
	    Strbuf_append(dir, *tw_env++);
	    Strbuf_append1(dir, '/');
	    Strbuf_terminate(dir);
	    ret = tw_dir_next(res, tw_dir_fd);
764 765
	}
    }
766
    return ret;
767 768 769 770 771 772 773
} /* end tw_file_next */


/* tw_dir_end():
 *	Clear directory related lists
 */
void
774
tw_dir_end(void)
775 776 777 778 779 780 781 782 783 784
{
   CLRDIR(tw_dir_fd)
   CLRDIR(tw_cmd_state.dfd)
} /* end tw_dir_end */


/* tw_item_free():
 *	Free the item list
 */
void
785
tw_item_free(void)
786 787 788 789 790 791 792 793 794
{
    tw_str_free(&tw_item);
} /* end tw_item_free */


/* tw_item_get(): 
 *	Return the list of items 
 */
Char **
795
tw_item_get(void)
796 797 798 799 800 801
{
    return tw_item.list;
} /* end tw_item_get */


/* tw_item_add():
802
 *	Return a new item for a Strbuf_terminate()'d s
803
 */
804 805
void
tw_item_add(const struct Strbuf *s)
806
{
807 808 809 810
    Char *p;

    p = tw_str_add(&tw_item, s->len + 1);
    Strcpy(p, s->s);
811 812 813 814 815 816 817 818
} /* tw_item_add */


/* tw_item_find():
 *      Find the string if it exists in the item list 
 *	end return it.
 */
Char *
819
tw_item_find(Char *str)
820
{
821
    size_t i;
822 823 824 825 826 827 828 829 830 831 832 833 834 835 836

    if (tw_item.list == NULL || str == NULL)
	return NULL;

    for (i = 0; i < tw_item.nlist; i++)
	if (tw_item.list[i] != NULL && Strcmp(tw_item.list[i], str) == 0)
	    return tw_item.list[i];
    return NULL;
} /* end tw_item_find */


/* tw_vl_start():
 *	Initialize a variable list
 */
void
837
tw_vl_start(DIR *dfd, const Char *pat)
838 839 840 841 842 843 844 845 846 847 848 849 850 851 852
{
    SETDIR(dfd)
    if ((tw_vptr = adrof(pat)) != NULL) {
	tw_env = tw_vptr->vec;
	tw_vptr = NULL;
    }
    else
	tw_env = NULL;
} /* end tw_vl_start */


/*
 * Initialize a word list
 */
void
853
tw_wl_start(DIR *dfd, const Char *pat)
854 855 856 857 858 859 860 861 862 863
{
    SETDIR(dfd);
    tw_word = pat;
} /* end tw_wl_start */


/*
 * Return the next word from the word list
 */
/*ARGSUSED*/
864 865
int
tw_wl_next(struct Strbuf *res, struct Strbuf *dir, int *flags)
866
{
867 868 869
    const Char *p;

    USE(dir);
870 871
    USE(flags);
    if (tw_word == NULL || tw_word[0] == '\0')
872 873
	return 0;

874 875
    while (*tw_word && Isspace(*tw_word)) tw_word++;

876
    for (p = tw_word; *tw_word && !Isspace(*tw_word); tw_word++)
877
	continue;
878 879 880
    if (tw_word == p)
	return 0;
    Strbuf_appendn(res, p, tw_word - p);
881
    if (*tw_word)
882 883
	tw_word++;
    return 1;
884 885 886 887 888 889 890 891
} /* end tw_wl_next */


/* tw_bind_start():
 *	Begin the list of the shell bindings
 */
/*ARGSUSED*/
void
892
tw_bind_start(DIR *dfd, const Char *pat)
893 894 895 896 897 898 899 900 901 902 903
{
    USE(pat);
    SETDIR(dfd)
    tw_bind = FuncNames;
} /* end tw_bind_start */


/* tw_bind_next():
 *	Begin the list of the shell bindings
 */
/*ARGSUSED*/
904 905
int
tw_bind_next(struct Strbuf *res, struct Strbuf *dir, int *flags)
906
{
907
    USE(dir);
908 909
    USE(flags);
    if (tw_bind && tw_bind->name) {
910 911 912 913
	const char *ptr;

	for (ptr = tw_bind->name; *ptr != '\0'; ptr++)
	    Strbuf_append1(res, *ptr);
914
	tw_bind++;
915
	return 1;
916
    }
917
    return 0;
918 919 920 921 922 923 924 925
} /* end tw_bind_next */


/* tw_limit_start():
 *	Begin the list of the shell limitings
 */
/*ARGSUSED*/
void
926
tw_limit_start(DIR *dfd, const Char *pat)
927 928 929 930 931 932 933 934 935 936 937 938 939
{
    USE(pat);
    SETDIR(dfd)
#ifndef HAVENOLIMIT
    tw_limit = limits;
#endif /* ! HAVENOLIMIT */
} /* end tw_limit_start */


/* tw_limit_next():
 *	Begin the list of the shell limitings
 */
/*ARGSUSED*/
940 941
int
tw_limit_next(struct Strbuf *res, struct Strbuf *dir, int *flags)
942
{
943 944
    USE(dir);
    USE(flags);
945 946
#ifndef HAVENOLIMIT
    if (tw_limit && tw_limit->limname) {
947 948 949 950
	const char *ptr;

	for (ptr = tw_limit->limname; *ptr != '\0'; ptr++)
	    Strbuf_append1(res, *ptr);
951
	tw_limit++;
952
	return 1;
953 954
    }
#endif /* ! HAVENOLIMIT */
955
    return 0;
956 957 958 959 960 961 962 963
} /* end tw_limit_next */


/* tw_sig_start():
 *	Begin the list of the shell sigings
 */
/*ARGSUSED*/
void
964
tw_sig_start(DIR *dfd, const Char *pat)
965 966 967 968 969 970 971 972 973 974 975
{
    USE(pat);
    SETDIR(dfd)
    tw_index = 0;
} /* end tw_sig_start */


/* tw_sig_next():
 *	Begin the list of the shell sigings
 */
/*ARGSUSED*/
976 977
int
tw_sig_next(struct Strbuf *res, struct Strbuf *dir, int *flags)
978
{
979
    USE(dir);
980 981
    USE(flags);
    for (;tw_index < nsig; tw_index++) {
982
	const char *ptr;
983 984 985 986

	if (mesg[tw_index].iname == NULL)
	    continue;

987 988
	for (ptr = mesg[tw_index].iname; *ptr != '\0'; ptr++)
	    Strbuf_append1(res, *ptr);
989
	tw_index++;
990
	return 1;
991
    }
992
    return 0;
993 994 995 996 997 998 999 1000
} /* end tw_sig_next */


/* tw_job_start():
 *	Begin the list of the shell jobings
 */
/*ARGSUSED*/
void
1001
tw_job_start(DIR *dfd, const Char *pat)
1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012
{
    USE(pat);
    SETDIR(dfd)
    tw_index = 1;
} /* end tw_job_start */


/* tw_job_next():
 *	Begin the list of the shell jobings
 */
/*ARGSUSED*/
1013 1014
int
tw_job_next(struct Strbuf *res, struct Strbuf *dir, int *flags)
1015 1016 1017
{
    struct process *j;

1018
    USE(dir);
1019 1020 1021 1022 1023 1024 1025
    USE(flags);
    for (;tw_index <= pmaxindex; tw_index++) {
	for (j = proclist.p_next; j != NULL; j = j->p_next)
	    if (j->p_index == tw_index && j->p_procid == j->p_jobid)
		break;
	if (j == NULL) 
	    continue;
1026
	Strbuf_append(res, j->p_command);
1027
	tw_index++;
1028
	return 1;
1029
    }
1030
    return 0;
1031
} /* end tw_job_next */