/* @(#)builtin.c	1.97 18/08/01 Copyright 1988-2018 J. Schilling */
#include <schily/mconfig.h>
#ifndef lint
static	UConst char sccsid[] =
	"@(#)builtin.c	1.97 18/08/01 Copyright 1988-2018 J. Schilling";
#endif
/*
 *	Builtin commands
 *
 *	Copyright (c) 1985-2018 J. Schilling
 */
/*
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * See the file CDDL.Schily.txt in this distribution for details.
 * A copy of the CDDL is also available via the Internet at
 * http://www.opensource.org/licenses/cddl1.txt
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file CDDL.Schily.txt from this distribution.
 */

#include <schily/stdio.h>
#include <schily/signal.h>
#include <schily/utypes.h>
#include <schily/resource.h>
#include "bsh.h"
#include "str.h"
#include "abbrev.h"
#include "strsubs.h"
#include "btab.h"
#include "map.h"
#include "node.h"
#include <schily/setjmp.h>
#include <schily/unistd.h>
#include <schily/stdlib.h>
#include <schily/fcntl.h>
#include <schily/ioctl.h>	/* Need to be before termios.h (BSD botch) */
#include <schily/termios.h>
#include <schily/utypes.h>
#include <schily/getargs.h>
#include <schily/time.h>
#include <schily/btorder.h>

extern int	firstsh;
extern int 	delim;
extern int 	ex_status;

extern	pid_t	opgrp;
extern	pid_t	mypgrp;
extern	pid_t	mypid;

LOCAL jmp_buf	waitjmp;

LOCAL	btab	*blook		__PR((char	*name, btab *bt, int n));
EXPORT	void	wrong_args	__PR((Argvec * vp, FILE ** std));
EXPORT	void	unimplemented	__PR((Argvec * vp, FILE ** std));
LOCAL	BOOL	not_loginsh	__PR((FILE ** std));
LOCAL	BOOL	helpwanted	__PR((Argvec * vp, FILE ** std));
EXPORT	BOOL	busage		__PR((Argvec * vp, FILE ** std));
EXPORT	BOOL	toint		__PR((FILE ** std, char *s, int *i));
EXPORT	BOOL	tolong		__PR((FILE ** std, char *s, long *l));
EXPORT	BOOL	tollong		__PR((FILE ** std, char *s, Llong *ll));
LOCAL	BOOL	isbadlpid	__PR((FILE ** std, long	lpid));
EXPORT	void	bnallo		__PR((Argvec * vp, FILE ** std, int flag));
EXPORT	void	bdummy		__PR((Argvec * vp, FILE ** std, int flag));
LOCAL	int	do_oset		__PR((const char *arg, void *valp, int *pac, char *const **pav, const char *opt));
LOCAL	int	do_ounset	__PR((const char *arg, void *valp, int *pac, char *const **pav, const char *opt));
EXPORT	void	bsetcmd		__PR((Argvec * vp, FILE ** std, int flag));
EXPORT	void	bunset		__PR((Argvec * vp, FILE ** std, int flag));
EXPORT	void	bsetenv		__PR((Argvec * vp, FILE ** std, int flag));
EXPORT	void	bunsetenv	__PR((Argvec * vp, FILE ** std, int flag));
EXPORT	void	bconcat		__PR((Argvec * vp, FILE ** std, int flag));
EXPORT	void	bmap		__PR((Argvec * vp, FILE ** std, int flag));
EXPORT	void	bunmap		__PR((Argvec * vp, FILE ** std, int flag));
EXPORT	void	bexit		__PR((Argvec * vp, FILE ** std, int flag));
EXPORT	void	beval		__PR((Argvec * vp, FILE ** std, int flag));
EXPORT	void	bdo		__PR((Argvec * vp, FILE ** std, int flag));
EXPORT	void	benv		__PR((Argvec * vp, FILE ** std, int flag));
EXPORT	void	bwait		__PR((Argvec * vp, FILE ** std, int flag));
LOCAL	sigret	waitint		__PR((int sig));
LOCAL	int	gsig		__PR((char *s, int *sigp));
EXPORT	void	bkill		__PR((Argvec * vp, FILE ** std, int flag));
LOCAL	void	killp		__PR((FILE ** std, BOOL grpflg, pid_t p, int sig));
EXPORT	void	bsuspend	__PR((Argvec * vp, FILE ** std, int flag));
EXPORT	void	bresume		__PR((Argvec * vp, FILE ** std, int flag));
EXPORT	void	bfg		__PR((Argvec * vp, FILE ** std, int flag));
EXPORT	void	bpgrp		__PR((Argvec * vp, FILE ** std, int flag));
EXPORT	void	bsync		__PR((Argvec * vp, FILE ** std, int flag));
EXPORT	void	bumask		__PR((Argvec * vp, FILE ** std, int flag));
EXPORT	void	bsetmask	__PR((Argvec * vp, FILE ** std, int flag));
LOCAL	char	*cvmode		__PR((int c, char *cp, char *ep, char *mode));
LOCAL	int	mval		__PR((mode_t m));
LOCAL	void	pmask		__PR((FILE ** std, BOOL do_posix));
EXPORT	void	blogout		__PR((Argvec * vp, FILE ** std, int flag));
EXPORT	void	becho		__PR((Argvec * vp, FILE ** std, int flag));
EXPORT	void	bshift		__PR((Argvec * vp, FILE ** std, int flag));
EXPORT	void	bremap		__PR((Argvec * vp, FILE ** std, int flag));
EXPORT	void	bsavehist	__PR((Argvec * vp, FILE ** std, int flag));
EXPORT	void	bhistory	__PR((Argvec * vp, FILE ** std, int flag));
EXPORT	void	bsource		__PR((Argvec * vp, FILE ** std, int flag));
EXPORT	void	brepeat		__PR((Argvec * vp, FILE ** std, int flag));
EXPORT	void	bexec		__PR((Argvec * vp, FILE ** std, int flag));
EXPORT	void	blogin		__PR((Argvec * vp, FILE ** std, int flag));
LOCAL	void	my_exec		__PR((char *name, int first_ac, Argvec *vp, FILE **std));
EXPORT	void	berrstr		__PR((Argvec * vp, FILE ** std, int flag));
EXPORT	void	btype		__PR((Argvec * vp, FILE ** std, int flag));
EXPORT	void	btrue		__PR((Argvec * vp, FILE ** std, int flag));
EXPORT	void	bfalse		__PR((Argvec * vp, FILE ** std, int flag));
#ifdef	DO_FIND
LOCAL	int	quitfun		__PR((void *arg));
#endif
EXPORT	BOOL	builtin		__PR((Argvec * vp, FILE ** std, int flag));
LOCAL	int	suspend		__PR((pid_t p));
LOCAL	int	presume		__PR((pid_t p));
LOCAL	mode_t	setcmask	__PR((mode_t newmask));
LOCAL	mode_t	getcmask	__PR((void));

/*
 * Lookup the builtin command table for a specific function
 */
LOCAL btab *
blook(name, bt, n)
	register char	*name;
	register btab	*bt;
		int	n;
{
	register char	*s1;
	register char	*s2;
	register int	mid;
	register int	low;
	register int	high;

	low = 0;
	high = n - 1;
	while (low <= high) {
		mid = (low + high) / 2;
		s1 = name;
		s2 = bt[mid].b_name;
		for (; *s1 == *s2; s1++, s2++) /* viel schneller als strcmp */
			if (*s1 == '\0')
				return (&bt[mid]);
		if (*s1 < *s2) {
			high = mid - 1;
			continue;
		}
		low = mid + 1;
	}
	return ((btab *) NULL);
}

/*
 * Generic function used when a builtin is called with a wrong number of args
 */
EXPORT void
wrong_args(vp, std)
	Argvec	*vp;
	FILE	*std[];
{
	fprintf(std[2], "%s: Wrong number of args.\n", vp->av_av[0]);
	busage(vp, std);
	ex_status = 1;
}

/*
 * Generic funtcion used to replace unimplemented funtions
 */
EXPORT void
unimplemented(vp, std)
	Argvec	*vp;
	FILE	*std[];
{
	fprintf(std[2], "%s: unimplemented.\n", vp->av_av[0]);
	ex_status = 1;
}

/*
 * Error function used when a function which is resereved for a login shell
 * is used from a non-login shell
 */
LOCAL BOOL
not_loginsh(std)
	FILE	*std[];
{
	if (!firstsh) {
		fprintf(std[2], "Not login shell.\n");
		ex_status = 1;
		return (TRUE);
	}
	return (FALSE);
}

/*
 * The generic function to check whether a builtin has been called
 * with the -help option
 */
LOCAL BOOL
helpwanted(vp, std)
	Argvec	*vp;
	FILE	*std[];
{
	if (vp->av_ac > 1 &&
	    (streql(vp->av_av[1], helpname) ||
	    (vp->av_av[1][0] == '-' && vp->av_av[1][1] == '-' &&
	    streql(&vp->av_av[1][1], helpname)))) {
		return (busage(vp, std));
	}
	return (FALSE);
}

/*
 * The generic function to print the usage for a builtin function.
 */
EXPORT BOOL
busage(vp, std)
	Argvec	*vp;
	FILE	*std[];
{
	register btab	*bp;
	register char	*name = vp->av_av[0];

	if ((bp = blook(name, bitab, n_builtin)) != NULL && bp->b_help) {
		if (bp->b_help == (char *)-1)
			return (FALSE);
		fprintf(std[2], "%s%s [options] %s\n", usage, name, bp->b_help);
		return (TRUE);
	}
	return (FALSE);
}

EXPORT BOOL
toint(std, s, i)
	FILE	*std[];
	char	*s;
	int	*i;
{
	if (*s == '\0' || *astoi(s, i)) {
		fprintf(std[2], "Not a number: %s.\n", s);
		ex_status = 1;
		return (FALSE);
	}
	return (TRUE);
}

EXPORT BOOL
tolong(std, s, l)
	FILE	*std[];
	char	*s;
	long	*l;
{
	if (*s == '\0' || *astol(s, l)) {
		fprintf(std[2], "Not a number: %s.\n", s);
		ex_status = 1;
		return (FALSE);
	}
	return (TRUE);
}

EXPORT BOOL
tollong(std, s, ll)
	FILE	*std[];
	char	*s;
	Llong	*ll;
{
	if (*s == '\0' || *astoll(s, ll)) {
		fprintf(std[2], "Not a number: %s.\n", s);
		ex_status = 1;
		return (FALSE);
	}
	return (TRUE);
}

LOCAL BOOL
isbadlpid(std, lpid)
	FILE	*std[];
	long	lpid;
{
	pid_t	p = lpid;

	if (p != lpid) {
		fprintf(std[2], "Bad process id: %ld.\n", lpid);
		ex_status = 1;
		return (TRUE);
	}
	return (FALSE);
}

/* ARGSUSED */
EXPORT void
bnallo(vp, std, flag)
	Argvec	*vp;
	FILE	*std[];
	int	flag;
{
	fprintf(std[2], "'%s' not allowed here.\n", vp->av_av[0]);
}

/* ARGSUSED */
EXPORT void
bdummy(vp, std, flag)
	Argvec	*vp;
	FILE	*std[];
	int	flag;
{
	/* this procedure does nothing */
}

struct seta {
	int	pfshell;
	char	*aliasowner;
	int	olist;
};

/* ARGSUSED */
LOCAL int
do_oset(arg, valp, pac, pav, opt)
	const char	*arg;
	void		*valp;
	int		*pac;
	char	*const	**pav;
	const char	 *opt;
{
	if (arg[0] == '-' || arg[0] == '+') {
		/*
		 * Strange Korn Shell rules:
		 * If next arg is an option, -o was called without parameter.
		 */
		if (arg == &opt[2])		/* arg concatenated with -o */
			return (BADFLAG);
		(*pav)--;
		(*pac)++;
		*(int *)valp = TRUE;
		((struct seta *)valp)->olist = TRUE;
		return (1);
	}
	if (streql(arg, "aliasowner")) {
		((struct seta *)valp)->aliasowner = "";
		return (1);
	} else if (strncmp(arg, "aliasowner=", 11) == 0) {
		((struct seta *)valp)->aliasowner = (char *)&arg[11];
		return (1);
	} else if (streql(arg, "profile")) {
		((struct seta *)valp)->pfshell = TRUE;
		return (1);
	}
	((struct seta *)valp)->olist = -1;
	return (BADFLAG);
}

/* ARGSUSED */
LOCAL int
do_ounset(arg, valp, pac, pav, opt)
	const char	*arg;
	void		*valp;
	int		*pac;
	char	*const	**pav;
	const char	 *opt;
{
	if (arg[0] == '-' || arg[0] == '+') {
		/*
		 * Strange Korn Shell rules:
		 * If next arg is an option, +o was called without parameter.
		 */
		if (arg == &opt[2])		/* arg concatenated with +o */
			return (BADFLAG);
		(*pav)--;
		(*pac)++;
		((struct seta *)valp)->olist = TRUE;
		return (1);
	}
	if (streql(arg, "aliasowner")) {
		((struct seta *)valp)->aliasowner = "";
		return (1);
	} else if (streql(arg, "profile")) {
		((struct seta *)valp)->pfshell = FALSE;
		return (1);
	}
	((struct seta *)valp)->olist = -1;
	return (BADFLAG);
}

/* ARGSUSED */
EXPORT void
bsetcmd(vp, std, flag)
	register	Argvec	*vp;
			FILE	*std[];
			int	flag;
{
	int	ac;
	char	* const *av;
	BOOL	help	= FALSE;
	struct seta	seta;
	int	ret;

	if (vp->av_ac <= 1) {
		ev_list(std[1]);
		return;
	}
	seta.pfshell	= pfshell;
	seta.aliasowner	= NULL;
	seta.olist	= FALSE;

	ac = vp->av_ac - 1;	/* set values */
	av = &vp->av_av[1];
	ret = getargs(&ac, &av, "help,P%1,+P%0,o&,+o&",
			&help,
			&seta.pfshell, &seta.pfshell,
			do_oset, &seta, do_ounset, &seta);

	/*
	 * The Korn Shell implements a really strange syntax.
	 * set -o uses an optional argument which is hard to parse with a
	 * standardized option parser.
	 */
	if (ret == BADFLAG && ac == 1 && seta.olist >= 0 &&
	    ((av[0][0] == '-' || av[0][0] == '+') && av[0][1] == 'o' && av[0][2] == '\0')) {
		seta.olist = TRUE;
	} else if (ret < 0 &&
		    (av[0][0] == '-' || av[0][0] == '+' || seta.olist < 0)) {
		fprintf(std[2], "Bad option '%s'\n", av[0]);
		busage(vp, std);
		ex_status = 1;
		return;
	}

	if (seta.aliasowner != NULL) {
		ab_setaltowner(GLOBAL_AB, seta.aliasowner);
		ab_setaltowner(LOCAL_AB, seta.aliasowner);
	}
	if (seta.pfshell != pfshell) {
		pfshell = seta.pfshell;
		if (pfshell)
			pfinit();
		else
			pfend();
	}

	if (ret != FLAGDELIM && ac == 1)
		ev_insert(makestr(av[0]));
	else if (ac > 1)
		wrong_args(vp, std);

	if (seta.olist) {
		uid_t	altuid = ab_getaltowner(GLOBAL_AB);
		char	*altuname = "";

		if (altuid != (uid_t)-1)
			altuname = ab_getaltoname(GLOBAL_AB);

		fprintf(std[1], "Current option settings\n");
		fprintf(std[1], "profile		%s\n", pfshell ? "on" : "off");
		fprintf(std[1], "aliasowner=%s\n", altuname);
	}
}

/* ARGSUSED */
EXPORT void
bunset(vp, std, flag)
	Argvec	*vp;
	FILE	*std[];
	int	flag;
{
	ev_delete(vp->av_av[1]);
}

/* ARGSUSED */
EXPORT void
bsetenv(vp, std, flag)
	register Argvec	*vp;
		FILE	*std[];
		int	flag;
{
	if (vp->av_ac == 1)
		ev_list(std[1]);
	else if (vp->av_ac != 3)
		wrong_args(vp, std);
	else
		ev_insert(concat(vp->av_av[1], eql, vp->av_av[2], (char *)NULL));
}

/* ARGSUSED */
EXPORT void
bunsetenv(vp, std, flag)
	Argvec	*vp;
	FILE	*std[];
	int	flag;
{
	ev_delete(vp->av_av[1]);
}

/* ARGSUSED */
EXPORT void
bconcat(vp, std, flag)
	register	Argvec	*vp;
			FILE	*std[];
			int	flag;
{
	register	char	*args;

	if (vp->av_ac < 3) {
		wrong_args(vp, std);
	} else {
		args = concatv(&vp->av_av[2]);
		ev_insert(concat(vp->av_av[1], eql, args, (char *)NULL));
		free(args);
	}
}

/* ARGSUSED */
EXPORT void
bmap(vp, std, flag)
	register	Argvec	*vp;
			FILE	*std[];
			int	flag;
{
#ifdef	INTERACTIVE
	if (vp->av_ac == 1)
		list_map(std[1]);
	else if (vp->av_ac == 2 && streql(vp->av_av[1], "-r"))
		remap();
	else if (vp->av_ac == 3 && streql(vp->av_av[1], "-u"))
		del_map(vp->av_av[2]);
	else if (vp->av_ac != 3 && vp->av_ac != 4)
		wrong_args(vp, std);
	else if (!add_map(vp->av_av[1], vp->av_av[2], vp->av_av[3])) {
		ex_status = 1;
		fprintf(std[2], "'%s' already defined.\n", vp->av_av[1]);
	}
#else
	unimplemented(vp, std);
#endif
}

/* ARGSUSED */
EXPORT void
bunmap(vp, std, flag)
	Argvec	*vp;
	FILE	*std[];
	int	flag;
{
#ifdef	INTERACTIVE
	del_map(vp->av_av[1]);
#else
	unimplemented(vp, std);
#endif
}

/* ARGSUSED */
EXPORT void
bexit(vp, std, flag)
	register	Argvec	*vp;
			FILE	*std[];
			int	flag;
{
	if (vp->av_ac > 2) {
		wrong_args(vp, std);
		return;
	}
	if (vp->av_ac == 2 && !toint(std, vp->av_av[1], &ex_status))
		return;
	exitbsh(ex_status);
}

/* ARGSUSED */
EXPORT void
beval(vp, std, flag)
	register	Argvec	*vp;
			FILE	*std[];
			int	flag;
{
	register int i;

	delim = ' ';
	pushline(nl);
	for (i = vp->av_ac; --i > 0; )
		pushline(vp->av_av[i]);
/*	freetree(cmdline(0|PGRP, std, FALSE));*/
	freetree(cmdline(flag, std, FALSE));
}

/*
 * Execute a command with new args
 */
/* ARGSUSED */
EXPORT void
bdo(vp, std, flag)
	register	Argvec	*vp;
			FILE	*std[];
			int	flag;
{
	int	sac = vac;			/* save old args */
	char	**sav = vav;
	BOOL	isdosh;

	isdosh = streql(vp->av_av[0], "dosh");

	if (isdosh) {
		/*
		 * dosh command_string [command_name [[args]]
		 */
		if (vp->av_ac <= 1)
			return;
		vac = vp->av_ac - 2;		/* set new args */
		vav = &vp->av_av[2];
	} else {
		/*
		 * do command_string [args]
		 */
		vac = vp->av_ac - 1;		/* set new args */
		vav = &vp->av_av[1];
	}
#ifdef DEBUGX
	printf("        bdo: executing '%s'\n", vp->av_av[1]); flush();
#endif
	pushline(vp->av_av[1]);
/*	freetree(cmdline(0|PGRP, std, FALSE));*/
	freetree(cmdline(flag, std, FALSE));
	vac = sac;
	vav = sav;
}

/*
 * Execute a command with new environment.
 * Also called for "name=value ...", in this case the ENV flag is set.
 */
/* ARGSUSED */
EXPORT void
benv(vp, std, flag)
	register	Argvec	*vp;
			FILE	*std[];
			int	flag;
{
extern	unsigned evasize;
extern	int	evaent;
	Argvec	*nvp;
	int	len;
	int	i;
	int	ac;
	char	* const *av;
	char	*p;
	char	*opt = "i";
	BOOL	iflag = FALSE;
	BOOL	keepenv = FALSE;
	pid_t	child;
volatile int	cstat;

	ac = len = vp->av_ac - 1;	/* set values */
	av = &vp->av_av[1];
	if (ac < 1) {
		ev_list(std[1]);
		return;
	}
	if ((flag & ENV) && strchr(av[ac-1], '=') != NULL) {
		for (i = 0; i < ac; i++) {
			if (strchr(av[i], '=') == NULL)
				break;
		}
		if (i == ac)
			keepenv = TRUE;	/* This is only a list of a=b args */

	} else if ((flag & ENV) == 0) {
		if (av[0][0] == '-' && av[0][1] == '\0') { /* non std '-' opt */
			iflag = TRUE;
			ac--;
			av++;
		}
		if (getargs(&ac, &av, opt, &iflag) < 0 && av[0][0] == '-') {
			busage(vp, std);
			ex_status = 1;
			return;
		}
	}

	/*
	 * Add new environment entries.
	 * This is only a list of a=b args - no real "env" command.
	 */
	if (keepenv) {
		for (; ac > 0 && (p = strchr(av[0], '=')) != NULL; ac--, av++) {
			if (ev_set_locked(av[0]))
				continue;
			ev_insert(makestr(av[0]));
		}
		return;
	}

	setstime();
	child = shfork(flag);
	if (child == 0) {

		if (iflag) {
			evarray = (char **)NULL;
			evasize = 0;
			evaent = 0;
			ev_inc();
		}
		/*
		 * Add new environment entries.
		 */
		for (; ac > 0 && (p = strchr(av[0], '=')) != NULL; ac--, av++) {
			if (ev_set_locked(av[0]))
				continue;
			ev_insert(makestr(av[0]));
		}
		if (ac < 1) {			/* If no args list env */
			ev_list(std[1]);
			_exit(0);
		}


		nvp = allocvec(ac);
		for (i = 0; i < ac; i++) {
			nvp->av_av[i] = vp->av_av[i + (len-ac) + 1];
		}
		cstat = execcmd(nvp, std, flag | (iflag ? IGNENV : 0));
		if (ex_status)
			_exit(ex_status);
		_exit(cstat);
		/*
		 * This is a (hidden) background command, so we wo not
		 * need to call: free(nvp);
		 */
	} else if (child != -1) {
		if (!(flag & ASYNC))
			ewait(child, WALL|NOTMS);
	}
}

/*
 * Wait for commands that have been previously executed in background.
 */
/* ARGSUSED */
EXPORT void
bwait(vp, std, flag)
	register	Argvec	*vp;
			FILE	*std[];
			int	flag;
{
	pid_t	p;
	long	lp;
	char	**t;
	volatile sigtype intsav = SIG_DFL;

	if (!setjmp(waitjmp)) {
		if (osig2 != (sigtype) SIG_IGN)
			intsav = signal(SIGINT, waitint);
		/*
		 * A longjmp() from the SIGINT handler may leave
		 * SIGINT blocked.
		 */
		unblock_sigs();
		if (vp->av_ac == 1) {
			ex_status = ewait((pid_t)0, 0);
		} else for (t = &vp->av_av[1]; *t != NULL; t++) {
			if (!tolong(std, *t, &lp))
				return;
			p = lp;
			if (isbadlpid(std, lp))
				return;
			ex_status = ewait(p, WALL);
		}
	}
	if (osig2 != (sigtype) SIG_IGN)
		signal(SIGINT, intsav);
}

/* ARGSUSED */
LOCAL sigret
waitint(sig)
	int	sig;
{
	extern int sigcount[];

	signal(SIGINT, waitint);
	sigcount[SIGINT]++;
	longjmp(waitjmp, TRUE);
}

LOCAL int
gsig(s, sigp)
	char	*s;
	int	*sigp;
{
	int	sig;

	if (*astoi(s, &sig)) {
		if (str2sig(s, &sig) != 0) {
			if (streql(s, "IOT")) {
				sig = SIGABRT;
			} else {
				return (-1);
			}
		}
	}
	*sigp = sig;
	if (sig > NSIG || sig < 0)
		return (-1);
	return (1);
}

/* ARGSUSED */
EXPORT void
bkill(vp, std, flag)
	register	Argvec	*vp;
		FILE	*std[];
		int	flag;
{
	int	ac;
	char	* const *av;
	char	*opt = "l,&";
	BOOL	list = FALSE;
	BOOL	kpgrp;
	pid_t	p;
	long	lp;
	int	sig = SIGTERM;
	int	i;
	register char * const *t;

	ac = vp->av_ac - 1;		/* set values */
	av = &vp->av_av[1];
	if (getargs(&ac, &av, opt, &list, gsig, &sig, &kpgrp) < 0) {
		if (sig > NSIG || sig < 0)
			fprintf(std[2], "%s: Bad signo: %d.", vp->av_av[0], sig);
		else
			fprintf(std[2], ebadopt, vp->av_av[0], av[0]);
		fprintf(std[2], " kill -l lists signals\n");
		busage(vp, std);
		ex_status = 1;
		return;
	}
	if (list) {
		int	maxsig = NSIG-1;

		if (ac > 0) {
			char	sname[32];
			char	*cp;

			for (i = 0; i < ac; i++) {
				sig = 0;
				cp = astoi(av[i], &sig);
				sig &= 0x7F;
				if (*cp || sig2str(sig, sname) < 0) {
					fprintf(std[2], "%s: Bad sig: '%s'.\n",
						vp->av_av[0],
						av[i]);
					ex_status = 1;
					return;
				}
				fprintf(std[1], "%s\n", sname);
			}
			return;
		}
#ifdef	SIGRTMAX
		if (SIGRTMAX > maxsig)
			maxsig = SIGRTMAX;
#endif
		for (i = 1; i <= maxsig; i++) {
			char	sname[32];

			if (sig2str(i, sname) == 0)
				fprintf(std[1], "%s ", sname);
			else
				fprintf(std[1], "%d ", i);
			if (i % 8 == 0)
				fprintf(std[1], "%s", nl);
		}
		fprintf(std[1], "%s", nl);
		return;
	}
	if (ac < 1) {
		wrong_args(vp, std);
		return;
	}
	kpgrp = streql(vp->av_av[0], "killpg");
	for (t = av; *t != NULL; t++) {
		if (!tolong(std, *t, &lp))
			return;
		p = lp;
		if (isbadlpid(std, lp))
			return;
		killp(std, kpgrp, p, sig);
	}
}

#	ifndef	HAVE_KILLPG
#		define	killpg(p, s)	kill(-(p), s)
#	endif

#ifdef	PROTOTYPES
LOCAL void
killp(FILE ** std, BOOL grpflg, pid_t p, int sig)
#else
LOCAL void
killp(std, grpflg, p, sig)
	FILE	*std[];
	BOOL	grpflg;
	pid_t	p;
	int	sig;
#endif
{
	if ((grpflg ? killpg(p, sig) : kill(p, sig)) < 0) {
		char	sname[32];

		ex_status = geterrno();
		fprintf(std[2], "Can't send ");
		if (sig2str(sig, sname) == 0)
			fprintf(std[2], "SIG%s ", sname);
		else
			fprintf(std[2], "signal %d ", sig);
		fprintf(std[2], "to %s %lld. %s\n",
			grpflg ? "processgroup" : "process", (Llong)p,
			errstr(ex_status));
	}
#ifdef	SIGCONT
	if (sig == SIGHUP || sig == SIGTERM)
		grpflg ? killpg(p, SIGCONT) : kill(p, SIGCONT);
#endif	/* SIGCONT */
}

/* ARGSUSED */
EXPORT void
bsuspend(vp, std, flag)
	register	Argvec	*vp;
			FILE	*std[];
			int	flag;
{
	pid_t	p = (pid_t)-1;
	long	lp;
	register char **t;
	sigtype intsav = (sigtype) SIG_DFL;
	int	stop;

#if	defined(SIGTSTP) || defined(JOS)

	stop = streql("stop", vp->av_av[0]);
	if (vp->av_ac == 1) {
		if (stop) {
			wrong_args(vp, std);
			return;
		}
		p = mypid;
		if (firstsh) {
			fprintf(std[2], "Can't suspend login shell.\n");
			ex_status = 1;
			return;
		}
#ifdef	SIGTSTP
		intsav = signal(SIGTSTP, (sigtype) SIG_DFL);
#endif
		if (suspend(p) < 0)
			ex_status = geterrno();
	} else for (t = &vp->av_av[1]; *t != NULL; t++) {
		if (!tolong(std, *t, &lp))
			return;
		p = lp;
		if (isbadlpid(std, lp))
			return;
		/* stop == killpg ???? */
		if (kill(p, stop ? SIGSTOP : SIGTSTP) < 0) {
			ex_status = geterrno();
			break;
		}
	}
#ifdef	SIGTSTP
	if (intsav != (sigtype) SIG_DFL)
		signal(SIGTSTP, intsav);
#endif
	if (ex_status != 0)
		fprintf(std[2], "Can't %s %lld. %s\n",
				vp->av_av[0], (Llong)p, errstr(ex_status));
#else	/* defined(SIGTSTP) || defined(JOS) */
	unimplemented(vp, std);
#endif	/* defined(SIGTSTP) || defined(JOS) */
}


/* ARGSUSED */
EXPORT void
bresume(vp, std, flag)
	register	Argvec	*vp;
			FILE	*std[];
			int	flag;
{
	pid_t	p;
	long	lp;
#ifdef	JOBCONTROL
	pid_t	pgrp;
#endif	/* JOBCONTROL */

#if	defined(SIGCONT) || defined(JOS)

	if (!tolong(std, vp->av_av[1], &lp))
		return;
	p = lp;
	if (isbadlpid(std, lp))
		return;
#ifdef	JOBCONTROL
	pgrp = getpgid(p);
	tty_setpgrp(0, pgrp);
#endif	/* JOBCONTROL */
	if (presume(p) < 0) {
		ex_status = geterrno();
		fprintf(std[2], "Can't resume %ld. %s\n", (long)p, errstr(ex_status));
	} else if (!(flag & ASYNC)) {
		ewait(p, WALL);
	}

#else	/* defined(SIGCONT) || defined(JOS) */

	unimplemented(vp, std);

#endif	/* defined(SIGCONT) || defined(JOS) */
}

/* ARGSUSED */
EXPORT void
bfg(vp, std, flag)
	register	Argvec	*vp;
			FILE	*std[];
			int	flag;
{
	int	fg;
	pid_t	p;
	long	lp;
	pid_t	pgrp;
#ifdef	JOBCONTROL
	extern pid_t	lastsusp;	/* XXX Temporary !!! */
#endif

#if	defined(SIGCONT)

	fg = streql("fg", vp->av_av[0]);
	if (vp->av_ac > 2) {
		wrong_args(vp, std);
		return;
	} else if (vp->av_ac == 1) {
#ifdef	JOBCONTROL
		if ((lp = lastsusp) == 0)
			return;
#endif
		if (streql("$", vp->av_av[0]))
			fg = TRUE;
	} else if (!tolong(std, vp->av_av[1], &lp))
		return;

	p = lp;
	if (isbadlpid(std, lp))
		return;
#if	!defined(HAVE_GETPGID) && !defined(HAVE_BSD_GETPGRP)
	if ((pgrp = -1) < 0) {
		seterrno(0);	/* XXX ??? */
		/*
		 * XXX We should have a local process table
		 * XXX to map pid to pgrp.
		 */
#else
	if ((pgrp = getpgid(p)) < 0) {
#endif
		ex_status = geterrno();
		fprintf(std[2], "Can't get processgroup of %ld. %s\n",
						(long)p, errstr(ex_status));
		return;
	}
#ifdef	JOBCONTROL
	if (fg)
		tty_setpgrp(0, pgrp);
#endif	/* JOBCONTROL */
	if (killpg(pgrp, SIGCONT) < 0) {
		ex_status = geterrno();
		fprintf(std[2], "Can't resume %ld. %s\n",
						(long)p, errstr(ex_status));
	}
	/*else*/ if (fg && !(flag & ASYNC))
		ewait(p, WALL);
#else	/* defined(SIGCONT) */
	unimplemented(vp, std);
#endif	/* defined(SIGCONT) */
}

/* ARGSUSED */
EXPORT void
bpgrp(vp, std, flag)
	register	Argvec	*vp;
			FILE	*std[];
			int	flag;
{
	pid_t	p;
	long	lp;
	pid_t	pgrp = 0;
#ifdef	HAVE_GETSID
	pid_t	sgrp;
#endif

	if (vp->av_ac > 2) {
		wrong_args(vp, std);
		return;
	} else if (vp->av_ac == 1) {
		lp = mypid;

#ifdef	TIOCGPGRP
		/*
		 * Prefer the ioctl() as the POSIX function tcgetpgrp() limits
		 * access in a way that we cannot accept.
		 */
		if (ioctl(fdown(std[0]), TIOCGPGRP, (char *)&pgrp) < 0)
			pgrp = -1;
#else
		pgrp = tty_getpgrp(fdown(std[0]));
#endif
#if	defined(HAVE_GETSID) && defined(HAVE_TCGETSID)
#ifdef	TIOCGSID
		/*
		 * Prefer the ioctl() as the POSIX function tcgetsid() limits
		 * access in a way that we cannot accept.
		 */
		if (ioctl(fdown(std[0]), TIOCGSID, (char *)&sgrp) < 0)
			sgrp = -1;
#else
		sgrp = tcgetsid(fdown(std[0]));
#endif
		fprintf(std[1], "ttyprocessgroup: %ld ttysessiongroup %ld\n",
				(long)pgrp, (long)sgrp);
#else
		fprintf(std[1], "ttyprocessgroup: %ld\n", (long)pgrp);
#endif
	} else if (!tolong(std, vp->av_av[1], &lp))
		return;
	p = lp;
	if (isbadlpid(std, lp))
		return;
#if	!defined(HAVE_GETPGID) && !defined(HAVE_BSD_GETPGRP)
	if (p != mypid) {
		unimplemented(vp, std);
		return;
	}
#endif
	seterrno(0);
	if ((pgrp = getpgid(p)) < 0 && geterrno() != 0) {
		ex_status = geterrno();
		fprintf(std[2], "Can't get processgroup of %ld. %s\n",
						(long)p, errstr(ex_status));
		return;
	}
	fprintf(std[1], "pid: %ld processgroup: %ld", (long)p, (long)pgrp);
#ifdef	HAVE_GETSID
	sgrp = getsid(p);
	fprintf(std[1], " sessiongroup: %ld", (long)sgrp);
#endif
	fprintf(std[1], "\n");
}

/* ARGSUSED */
EXPORT void
bsync(vp, std, flag)
	Argvec *vp;
	FILE	*std[];
	int	flag;
{
#ifdef	HAVE_SYNC
	sync();
#else
	unimplemented(vp, std);
#endif
}

/* ARGSUSED */
EXPORT void
bumask(vp, std, flag)
	register	Argvec *vp;
	register	FILE	*std[];
			int	flag;
{
		int	ac;
		char	* const *av;
		BOOL	symbolic = FALSE;
		mode_t	newmask = 0;
	register char	*cp;

	ac = vp->av_ac - 1;		/* set values */
	av = &vp->av_av[1];
	if (getargs(&ac, &av, "S", &symbolic) < 0) {
		/*
		 * Let the last argument fail in getperm()
		 * if it is not OK. POSIX requires "umask -r" to fail,
		 * this test catches e.g. "umask a=rwx".
		 */
		if (ac == 1 && vp->av_ac >= 2 && av[0][0] != '-')
			goto ok;
		fprintf(std[2], ebadopt, vp->av_av[0], av[0]);
		fprintf(std[2], "%s", nl);
		busage(vp, std);
		ex_status = 1;
		return;
	}
	if (ac == 0) {
		if (symbolic)
			pmask(std, TRUE);
		else
			fprintf(std[1], "0%llo\n", (ULlong)getcmask());
		return;
	}
	if (ac != 1) {
		wrong_args(vp, std);
		return;
	}
ok:
	if (getperm(std[2], av[0], NULL, &newmask, ~getcmask(),
							GP_UMASK|GP_NOX)) {
		fprintf(std[2], "Improper mask\n");
		ex_status = 1;
		return;
	}
	cp = av[0];
	if (*cp >= '0' && *cp <= '7')
		(void) setcmask(newmask);
	else
		(void) setcmask(~newmask);
}

/* ARGSUSED */
EXPORT void
bsetmask(vp, std, flag)
	register	Argvec *vp;
	register	FILE	*std[];
			int	flag;
{
			char	mstr[64];
			char	*mp;
			mode_t	 oldmask;
			mode_t	 newmask;
	register	char	 **av = vp->av_av;

	if (vp->av_ac == 1) {
		pmask(std, FALSE);
		return;
	}
	if (vp->av_ac != 4) {
		wrong_args(vp, std);
		return;
	}
	oldmask = ~getcmask();
	mp = mstr;
	mp = cvmode('u', mp, &mstr[sizeof (mstr) - 1], av[1]);
	mp = cvmode('g', mp, &mstr[sizeof (mstr) - 1], av[2]);
	mp = cvmode('o', mp, &mstr[sizeof (mstr) - 1], av[3]);
	if (mp > mstr && mp[-1] == ',')
		mp[-1] = '\0';
	if (mstr[0] == '\0')	/* Keep old mask */
		return;
	if (getperm(std[2], mstr, NULL, &newmask, oldmask, GP_NOX)) {
		fprintf(std[2], "Improper mask\n");
		ex_status = 1;
		return;
	}
	(void) setcmask(~newmask);
}

LOCAL char *
cvmode(c, cp, ep, mode)
	int	c;
	char	*cp;
	char	*ep;
	char	*mode;
{
	char	*p = cp;
	char	*mp;

	for (mp = mode; *mp; mp++) {
		if (*mp == '=') {	/* Keep old mask */
			p = cp;
			mp++;
			if (*mp == '\0')
				break;
		}
		if (p == cp) {		/* Add perm type */
			*p++ = c;
		}
		if (*mp == '.') {	/* Clear mask */
			if ((p+5) >= ep)
				break;
			strcpy(p, "-rwx+");
			p += 5;
			continue;
		} else if (p == &cp[1] && *mp != '-' && *mp != '+') {
			*p++ = '=';
		}
		if (p >= ep)
			break;
		*p++ = *mp;
	}
	if (p > cp)
		*p++ = ',';
	*p = '\0';
	return (p);
}


static char *modtab[] =
{"...", "..x", ".w.", ".wx", "r..", "r.x", "rw.", "rwx"};
static char *umodtab[] =
{"", "x", "w", "wx", "r", "rx", "rw", "rwx"};


#ifdef	PROTOTYPES
LOCAL int
mval(mode_t m)
#else
LOCAL int
mval(m)
	mode_t	m;
#endif
{
	int	ret = 0;

	if (m & (S_IRUSR|S_IRGRP|S_IROTH))
		ret |= 4;
	if (m & (S_IWUSR|S_IWGRP|S_IWOTH))
		ret |= 2;
	if (m & (S_IXUSR|S_IXGRP|S_IXOTH))
		ret |= 1;

	return (ret);
}

LOCAL void
pmask(std, do_posix)
	FILE	*std[];
	BOOL	do_posix;
{
	mode_t	m;

	m = getcmask();
	m = ~m;

	if (do_posix)
		fprintf(std[1], "u=%s,g=%s,o=%s\n",
			umodtab[mval(m & S_IRWXU)],
			umodtab[mval(m & S_IRWXG)],
			umodtab[mval(m & S_IRWXO)]);
	else
		fprintf(std[1], "%s %s %s\n",
			modtab[mval(m & S_IRWXU)],
			modtab[mval(m & S_IRWXG)],
			modtab[mval(m & S_IRWXO)]);
}

/* ARGSUSED */
EXPORT void
blogout(vp, std, flag)
	Argvec	*vp;
	FILE	*std[];
	int	flag;
{
	if (not_loginsh(std)) {
		fprintf(std[2], "use exit to exit.\n");
		ex_status = 1;
		return;
	}
	exitbsh(0);
}

/* ARGSUSED */
EXPORT void
becho(vp, std, flag)
	register Argvec	*vp;
	register FILE	*std[];
		int	flag;
{
	register int	i = 1;
	register int	first = 1;
	register int	ac = vp->av_ac;
	register char **av = vp->av_av;
	register FILE	*output;
		char	buf[4096];
		BOOL	nnl;
		BOOL	glob;

	glob = streql(av[0], "glob");
	output = streql(av[0], "err") ? std[2] : std[1];

	if (!glob && std[0] != stdin && ac == 1) {
		file_raise(std[0], FALSE);
		file_raise(output, FALSE);
		do {
			i = fileread(std[0], buf, sizeof (buf));
			if (i > 0)
				filewrite(output, buf, i);
		} while (i > 0 && !ctlc);
	} else {
		nnl = glob;
		if (!glob && ac > 1 && (streql(av[1], "-nnl")||streql(av[1], "-n")))
			i++, nnl = TRUE;
		for (; i < ac && !ctlc; i++) {
			if (first)
				first--;
			else
				glob ? fputc('\0', output) : fprintf(output, " ");
			fprintf(output, "%s", av[i]);
		}
		if (!nnl && ac > 1)
			fprintf(output, "%s", nl);
	}
}

/* ARGSUSED */
EXPORT void
bshift(vp, std, flag)
	register	Argvec	*vp;
			FILE	*std[];
			int	flag;
{
	register int 	i,
			k;
		int	count = 1;

	if (vp->av_ac > 2) {
		wrong_args(vp, std);
		return;
	}
	if (vp->av_ac == 2)
		if (!toint(std, vp->av_av[1], &count))
			return;
	for (k = 0; k < count; k++) {
		if (vac <= 1) {
			if (vac)
				fprintf(std[2], "%s: ", vav[0]);
			fprintf(std[2], "cannot shift.\n");
			ex_status = 1;
			return;
		}
		for (i = 1; i <= vac; i++)
			vav[i] = vav[i+1];
		vac--;
	}
}

/* ARGSUSED */
EXPORT void
bremap(vp, std, flag)
	Argvec	*vp;
	FILE	*std[];
	int	flag;
{
#ifdef	INTERACTIVE
	remap();
#else
	unimplemented(vp, std);
#endif
}

#ifdef	INTERACTIVE
/* ARGSUSED */
EXPORT void
bsavehist(vp, std, flag)
	Argvec	*vp;
	FILE	*std[];
	int	flag;
{
	save_history(HI_INTR);
}
#endif	/* INTERACTIVE */

/* ARGSUSED */
EXPORT void
bhistory(vp, std, flag)
	Argvec	*vp;
	FILE	*std[];
	int	flag;
{
	int	ac;
	char	* const *av;
	int	ret;
	BOOL	nflag = FALSE;
	BOOL	rflag = FALSE;

	ac = vp->av_ac - 1;	/* set values */
	av = &vp->av_av[1];
	ret = getargs(&ac, &av, "n,r", &nflag, &rflag);
	if (ret < 0) {
		fprintf(std[2], "Bad option '%s'\n", av[0]);
		busage(vp, std);
		ex_status = 1;
		return;
	}

#ifdef	INTERACTIVE
	put_history(std[1],
		HI_INTR|HI_TAB|
		(nflag ? HI_NONUM : 0)|
		(rflag ? HI_REVERSE : 0), 0, 0, NULL);
#else
	hi_list(std[1]);
#endif
}

/* ARGSUSED */
EXPORT void
bsource(vp, std, flag)
	register	Argvec	*vp;
			FILE	*std[];
			int	flag;
{
	extern	int	do_status;
		int	ac = vp->av_ac;
		char	*name = NULL;	/* Init to make gcc happy */
		char	*pname = NULL;
	register FILE	*f;

	if (ac < 2) {
#ifndef	NO_STDIN_SOURCE
		if (isatty(fdown(std[0]))) {
			wrong_args(vp, std);
			return;
		}
		doopen(std[0], "stdin", GLOBAL_AB, flag, std, FALSE);
		return;
#else
		wrong_args(vp, std);
		return;
#endif
	}
	if (vp->av_av[0][0] != '.' &&
	    streql(vp->av_av[1], "-h")) {
		if (ac > 3) {
			wrong_args(vp, std);
			return;
		}
		if (ac == 3) {
			/*
			 * Be careful and do no PATH name search when
			 * reading a shell script into the history.
			 * We like to avoid that "source -h ls" fills
			 * the history with junk.
			 */
			name = vp->av_av[2];
			if ((f = fileopen(name, for_read)) == (FILE *) NULL)
				ex_status = geterrno();
		} else {
			name = "stdin";
			f = std[0];
		}
		if (f) {
			readhistory(f);
			if (f == stdin)
				clearerr(f);
			if (f != std[0])
				fclose(f);
		}
	} else {
		pname = name = _findinpath(vp->av_av[1], R_OK, TRUE);
		if (name) {
			dofile(name, GLOBAL_AB, flag, std, FALSE);
			ex_status = do_status;
		} else {
			ex_status = geterrno();
		}
	}
	if (ex_status != 0) {
		fprintf(std[2], ecantread, name, errstr(ex_status));
		fprintf(std[2], "%s", nl);
	}
	if (pname)
		free(pname);
}

/* repeat command - execute command several times */
/* ARGSUSED */
EXPORT void
brepeat(vp, std, flag)
	register	Argvec	*vp;
	register	FILE	*std[];
			int	flag;
{
		int	ac;
		char	* const *av;
		long	count	= 0x7FFFFFFF;
		long	dtime	= 0;
	volatile long	sttime	= 0;
		long	d;
	Tnode	* volatile cmd	= (Tnode *) NULL;
		char	*opt	= "delay#L,d#L,count#L,c#L,#L";
		char	*cmdln;
	volatile sigtype intsav = SIG_DFL;

	ac = vp->av_ac - 1;		/* set values */
	av = &vp->av_av[1];
	if (getargs(&ac, &av, opt, &dtime, &dtime,
						&count, &count, &count) < 0) {
		fprintf(std[2], ebadopt, vp->av_av[0], av[0]);
		fprintf(std[2], "%s", nl);
		busage(vp, std);
		ex_status = 1;
		return;
	}
	if (ac < 1) {
		wrong_args(vp, std);
		return;
	}
	if ((cmdln = cons_args(vp, vp->av_ac - ac)) == NULL) {
		ex_status = 1;
		return;
	}
	if (!setjmp(waitjmp)) {
		if (osig2 != (sigtype) SIG_IGN)
			intsav = signal(SIGINT, waitint);
		/*
		 * A longjmp() from the SIGINT handler may leave
		 * SIGINT blocked.
		 */
		unblock_sigs();
		while (count-- > 0) {
			if (dtime) {
				if (sttime) {
					d = time(0) - sttime;
					if (d < dtime)
						sleep(dtime - d);
				}
				sttime = time(0);
			}
#ifdef DEBUGX
		printf("        brepeat: executing '%s'\n", cmdln); flush();
#endif
			if (!cmd) {
				pushline(cmdln);
/*				cmd = cmdline(0|PGRP, std, TRUE);*/
				cmd = cmdline(flag, std, TRUE);
			}
/*			execute(cmd, 0|PGRP, std);*/
			execute(cmd, flag, std);
#ifdef	JOBCONTROL
			tty_setpgrp(0, mypgrp);
#endif	/* JOBCONTROL */
			if (ctlc)
				ex_status = 1;
			if (ex_status)
				break;
		}
	}
	if (osig2 != (sigtype) SIG_IGN)
		signal(SIGINT, intsav);
	free(cmdln);
	freetree(cmd);
}

/* ARGSUSED */
EXPORT void
bexec(vp, std, flag)
	Argvec	*vp;
	FILE	*std[];
	int	flag;
{
	my_exec(vp->av_av[1], 1, vp, std);
}

/* ARGSUSED */
EXPORT void
blogin(vp, std, flag)
	Argvec	*vp;
	FILE	*std[];
	int	flag;
{
	if (!not_loginsh(std)) {
#ifdef	INTERACTIVE
		if (!no_histflg)
			save_history(HI_NOINTR);
#endif	/* INTERACTIVE */
		my_exec(loginname, 0, vp, std);
	}
}

LOCAL void
my_exec(name, first_ac, vp, std)
			char	*name;
			int	first_ac;
	register	Argvec	*vp;
			FILE	*std[];
{
	char	**av = &vp->av_av[first_ac];
	int	ac   = vp->av_ac - first_ac;
	char	*av0 = NULL;

	if (getargs(&ac, (char * const **)&av, "av0*", &av0) < 0) {
		fprintf(std[2], ebadopt, vp->av_av[0], av[0]);
		fprintf(std[2], "%s", nl);
		busage(vp, std);
		ex_status = 1;
		return;
	}
	if (ac < 1) {
		wrong_args(vp, std);
		return;
	}
	name = makestr(name);
	if (av0) {
		free(name);
		name = makestr(av[0]);
		av0 = makestr(av0);
		free(av[0]);
		av[0] = av0;
	}
	update_cwd();			/* Er koennte sie veraendert haben */
	setpgid(0, opgrp);
#ifdef	JOBCONTROL
	tty_setpgrp(fdown(std[0]), opgrp);
#endif	/* JOBCONTROL */
	ex_status = fexec((char **)NULL, name, std[0], std[1], std[2],
								av, evarray);
	setpgid(0, mypgrp);
#ifdef	JOBCONTROL
	tty_setpgrp(fdown(std[0]), mypgrp);
#endif	/* JOBCONTROL */
	fprintf(std[2], "Can't exec '%s'. %s\n", name, errstr(ex_status));
	free(name);
}

/* ARGSUSED */
EXPORT void
berrstr(vp, std, flag)
	Argvec	*vp;
	FILE	*std[];
	int	flag;
{
	int	err;

	if (!toint(std, vp->av_av[1], &err))
		return;
	fprintf(std[1], "%s", errstr(err));
}

/* ARGSUSED */
EXPORT void
btype(vp, std, flag)
	Argvec	*vp;
	FILE	*std[];
	int	flag;
{
	register int	ac = vp->av_ac;
	register char **av = vp->av_av;
	register int	i;
	register FILE	*output = std[1];
	char		*val	= NULL;

	if (vp->av_ac < 2) {
		wrong_args(vp, std);
		return;
	}

	for (i = 1; i < ac && !ctlc; i++) {
		fprintf(output, "%s ", av[i]);
		if ((val = ab_value(LOCAL_AB, av[i], NULL, AB_BEGIN)) != NULL) {
			fprintf(output, "is a local alias to '%s'\n", val);
		} else if ((val = ab_value(GLOBAL_AB, av[i], NULL, AB_BEGIN)) != NULL) {
			fprintf(output, "is a global alias to '%s'\n", val);
		} else if (blook(av[i], bitab, n_builtin) != (btab *) NULL) {
			fprintf(output, "is a shell builtin\n");
		} else if ((val = map_func(av[i])) != NULL) {
			fprintf(output, "is a function '%s'\n", val);
		} else if ((val = _findinpath(av[i], X_OK, TRUE)) != NULL) {
			fprintf(output, "is %s\n", val);
			free(val);
			val = NULL;
		} else {
			fprintf(output, "not found\n");
			ex_status = 1;
		}
	}
}

/* ARGSUSED */
EXPORT void
btrue(vp, std, flag)
	Argvec	*vp;
	FILE	*std[];
	int	flag;
{
	ex_status = 0;
}

/* ARGSUSED */
EXPORT void
bfalse(vp, std, flag)
	Argvec	*vp;
	FILE	*std[];
	int	flag;
{
	ex_status = 1;
}

#ifdef	DO_FIND
#include <schily/walk.h>
#include <schily/find.h>

LOCAL int
quitfun(arg)
	void	*arg;
{
	return (*((int *)arg) != 0);
}

/* ARGSUSED */
EXPORT void
bfind(vp, std, flag)
	Argvec	*vp;
	FILE	*std[];
	int	flag;
{
	squit_t	quit;

	find_sqinit(&quit);
	quit.quitfun = quitfun;
	quit.qfarg = &ctlc;
	quit.flags = 0;
	ex_status = find_main(vp->av_ac, vp->av_av, evarray, std, &quit);
}
#endif

/* ARGSUSED */
EXPORT BOOL
builtin(vp, std, flag)
	Argvec	*vp;
	FILE	*std[];
	int	flag;
{
	register btab	*bp;
	register char	*name = vp->av_av[0];
		struct rusage	ru1;
		struct rusage	ru2;
		struct rusage	cru1;
		struct rusage	cru2;

	if (ctlc)
		return (TRUE);
	if ((bp = blook(name, bitab, n_builtin)) == (btab *) NULL)
		return (FALSE);
	setstime();
	getpruself(&ru1);
	getpruchld(&cru1);
	ex_status = 0;
	if (!helpwanted(vp, std)) {
		if (bp->b_argc && vp->av_ac != bp->b_argc)
			wrong_args(vp, std);
		else
			(*bp->b_func)(vp, std, flag);
	}
	fflush(std[1]);
	fflush(std[2]);
	if ((flag & NOTMS) == 0 && getcurenv("TIME")) {
		getpruself(&ru2);
		getpruchld(&cru2);
		rusagesub(&ru1, &ru2);
		rusagesub(&cru1, &cru2);
		rusageadd(&cru2, &ru2);
		prtimes(gstd, &ru2);
	}
	return (TRUE);
}

#ifdef	PROTOTYPES
LOCAL int
suspend(pid_t p)
#else
LOCAL int
suspend(p)
	pid_t	p;
#endif
{
#ifdef	SIGSTOP
	return (kill(p, SIGSTOP));
#else
	raisecond("suspend not implemented", 0L);
#endif
}

#ifdef	PROTOTYPES
LOCAL int
presume(pid_t p)
#else
LOCAL int
presume(p)
	pid_t	p;
#endif
{
#ifdef	SIGCONT
	if (p == getpid()) {
		seterrno(ESRCH);
		return (-1);
	}
	return (kill(p, SIGCONT));
#else
	raisecond("presume not implemented", 0L);
#endif
}

#ifdef	PROTOTYPES
LOCAL mode_t
setcmask(mode_t newmask)
#else
LOCAL mode_t
setcmask(newmask)
	mode_t	newmask;
#endif
{
	return (umask(newmask));
}

LOCAL mode_t
getcmask()
{
	mode_t	ret =  umask(0);

	umask(ret);
	return (ret);
}
