/*
 *	posixsh.c
 *
 *	XSH command for POSIX
 */

#include <fcntl.h>
#ifdef	FD
#include "fd.h"
#else	/* !FD */
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "machine.h"
#include "printf.h"
#include "kctype.h"
#include "pathname.h"

#ifndef	NOUNISTDH
#include <unistd.h>
#endif

#ifndef	NOSTDLIBH
#include <stdlib.h>
#endif
#endif	/* !FD */

#if	MSDOS
#include <io.h>
#include "unixemu.h"
#else	/* !MSDOS */
#include <sys/param.h>
#include <sys/ioctl.h>
#endif	/* !MSDOS */

#include "system.h"

#if	!defined (MINIMUMSHELL) \
&& (!defined (FD) || (FD >= 2 && !defined (_NOORIGSHELL)))

#ifdef	FD
#include "term.h"
extern int Xstat __P_((char *, struct stat *));
extern int Xlstat __P_((char *, struct stat *));
extern int Xaccess __P_((char *, int));
#else	/* !FD */
extern int ttyio;
extern FILE *ttyout;
# if	MSDOS
extern int Xstat __P_((char *, struct stat *));
# define	Xlstat		Xstat
# else
# define	Xstat		stat
# define	Xlstat		lstat
# endif
#define	Xaccess(p, m)	(access(p, m) ? -1 : 0)
#endif	/* !FD */

#ifndef	EPERM
#define	EPERM		EACCES
#endif

extern VOID error __P_((char *));
extern char *malloc2 __P_((ALLOC_T));
extern char *realloc2 __P_((VOID_P, ALLOC_T));
extern char *strdup2 __P_((char *));
extern char *strndup2 __P_((char *, int));
extern char *strchr2 __P_((char *, int));
extern char *strncpy2 __P_((char *, char *, int));
extern int setenv2 __P_((char *, char *, int));
extern time_t time2 __P_((VOID_A));

#ifndef	NOJOB
static char *NEAR headstree __P_((syntaxtree *));
#endif
#ifndef	NOALIAS
static shaliastable *NEAR duplalias __P_((shaliastable *));
static int cmpalias __P_((CONST VOID_P, CONST VOID_P));
#endif
static int NEAR _evalexpression __P_((char *, int, long *));
static int NEAR evalexpression __P_((char *, int, long *, int));
#if	!MSDOS
static VOID NEAR addmailpath __P_((char *, char *, time_t));
#endif
static int NEAR testsub1 __P_((int, char *, int *));
static int NEAR testsub2 __P_((int, char *[], int *));
static int NEAR testsub3 __P_((int, char *[], int *, int));

#define	ALIASDELIMIT	"();&|<>"
#define	BUFUNIT		32
#define	getconstvar(s)	(getshellvar(s, sizeof(s) - 1))
#define	ER_COMNOFOUND	1
#define	ER_NOTFOUND	2
#define	ER_NOTIDENT	4
#define	ER_BADOPTIONS	13
#define	ER_MISSARG	15
#define	ER_NOTALIAS	19
#define	ER_NOSUCHJOB	20
#define	ER_NUMOUTRANGE	21
#define	ER_UNKNOWNSIG	22

#if	!MSDOS
typedef struct _mailpath_t {
	char *path;
	char *msg;
	time_t mtime;
} mailpath_t;
#endif

#ifndef	NOJOB
int gettermio __P_((p_id_t));
VOID dispjob __P_((int, FILE *));
int searchjob __P_((p_id_t, int *));
int getjob __P_((char *));
int stackjob __P_((p_id_t, int, syntaxtree *));
int stoppedjob __P_((p_id_t));
VOID killjob __P_((VOID_A));
VOID checkjob __P_((int));
#endif	/* !NOJOB */
char *evalposixsubst __P_((char *, int *));
#if	!MSDOS
VOID replacemailpath __P_((char *, int));
VOID checkmail __P_((int));
#endif
#ifndef	NOALIAS
VOID freealias __P_((shaliastable *));
int checkalias __P_((syntaxtree *, char *, int, int));
#endif
#ifndef	NOJOB
int posixjobs __P_((syntaxtree *));
int posixfg __P_((syntaxtree *));
int posixbg __P_((syntaxtree *));
#endif
#ifndef	NOALIAS
int posixalias __P_((syntaxtree *));
int posixunalias __P_((syntaxtree *));
#endif
int posixkill __P_((syntaxtree *));
int posixtest __P_((syntaxtree *));
#ifndef	NOPOSIXUTIL
int posixcommand __P_((syntaxtree *));
int posixgetopts __P_((syntaxtree *));
#endif

#ifndef	NOJOB
jobtable *joblist = NULL;
int maxjobs = 0;
#endif
#if	!MSDOS
int mailcheck = 0;
#endif
#ifndef	NOALIAS
shaliastable *shellalias = NULL;
#endif
#ifndef	NOPOSIXUTIL
int posixoptind = 0;
#endif

#if	!MSDOS
static mailpath_t *mailpathlist = NULL;
static int maxmailpath = 0;
#endif


#ifndef	NOJOB
int gettermio(pgrp)
p_id_t pgrp;
{
	int ret;
	sigmask_t mask, omask;

	if (ttypgrp < (p_id_t)0 || pgrp < (p_id_t)0 || pgrp == ttypgrp)
		return(0);
	else if (!jobok) {
		ttypgrp = pgrp;
		return(0);
	}

	Xsigemptyset(mask);
#ifdef	SIGTSTP
	Xsigaddset(mask, SIGTSTP);
#endif
#ifdef	SIGCHLD
	Xsigaddset(mask, SIGCHLD);
#endif
#ifdef	SIGTTIN
	Xsigaddset(mask, SIGTTIN);
#endif
#ifdef	SIGTTOU
	Xsigaddset(mask, SIGTTOU);
#endif
	Xsigblock(omask, mask);

#ifdef	JOBVERBOSE
	fprintf2(ttyout, "gettermio: %id: %id -> %id", mypid, ttypgrp, pgrp);
	fputnl(ttyout);
#endif	/* JOBVERBOSE */
	if ((ret = settcpgrp(ttyio, pgrp)) >= 0) ttypgrp = pgrp;
	Xsigsetmask(omask);
	return(ret);
}

static char *NEAR headstree(trp)
syntaxtree *trp;
{
	int id;

	if (!trp) return(NULL);
	if (trp -> flags & ST_NODE) return("{");
	if (!(trp -> comm)) return(NULL);
	if (!isstatement(trp -> comm)) return((trp -> comm) -> argv[0]);

	id = (trp -> comm) -> id;
	if (id != SM_STATEMENT) return(NULL);
	trp = statementbody(trp);
	if (!trp || !(trp -> comm)) return(NULL);
	id = (trp -> comm) -> id;
	if (id == SM_CHILD || id == SM_STATEMENT) return(NULL);
	return(statementlist[id - 1].ident);
}

VOID dispjob(n, fp)
int n;
FILE *fp;
{
	int i, sig;

	if (n < 0 || n >= maxjobs || !(joblist[n].pids)) return;
	i = joblist[n].npipe;
	fprintf2(fp, "[%d]%c %id ",
		n + 1, (n == lastjob) ? '+' : ((n == prevjob) ? '-' : ' '),
		joblist[n].pids[i]);
	sig = joblist[n].stats[i];

	if (sig <= 0)
		fprintf2(fp, "%-28.28s", (sig) ? "Done" : "Running");
	else {
		if (sig >= 128) sig -= 128;
		dispsignal(sig, 28, fp);
	}
	if (joblist[n].trp) {
		if (!isopbg(joblist[n].trp) && !isopnown(joblist[n].trp)
		&& !isoppipe(joblist[n].trp))
			joblist[n].trp -> type = OP_NONE;
		printstree(joblist[n].trp, -1, fp);
	}
	fputnl(fp);
}

int searchjob(pid, np)
p_id_t pid;
int *np;
{
	int i, j;

	for (i = 0; i < maxjobs; i++) {
		if (!(joblist[i].pids)) continue;
		for (j = 0; j <= joblist[i].npipe; j++) {
			if (joblist[i].pids[j] != pid) continue;
			if (np) *np = j;
			return(i);
		}
	}
	return(-1);
}

int getjob(s)
char *s;
{
	char *cp;
	int i, j;

	if (!jobok) return(-1);
	if (!s) i = lastjob;
	else {
		if (s[0] != '%') return(-1);
		if (!s[1] || ((s[1] == '%' || s[1] == '+') && !s[2]))
			i = lastjob;
		else if (s[1] == '-' && !s[2]) i = prevjob;
		else if ((i = isnumeric(&(s[1]))) >= 0) i--;
		else {
			j = strlen(&(s[1]));
			for (i = 0; i < maxjobs; i++) {
				if (!(joblist[i].pids) || !(joblist[i].trp)
				|| !(cp = headstree(joblist[i].trp)))
					continue;
				if (!strnpathcmp(&(s[1]), cp, j)) break;
			}
		}
	}
	if (i < 0 || i >= maxjobs || !joblist || !(joblist[i].pids))
		return(-1);
	j = joblist[i].npipe;
	if (joblist[i].stats[j] < 0 || joblist[i].stats[j] >= 128) return(-1);

	return(i);
}

int stackjob(pid, sig, trp)
p_id_t pid;
int sig;
syntaxtree *trp;
{
	int i, j, n;

#ifdef	FAKEUNINIT
	j = 0;	/* fake for -Wuninitialized */
#endif
	if (!joblist) {
		joblist = (jobtable *)malloc2(BUFUNIT * sizeof(jobtable));
		maxjobs = BUFUNIT;
		i = 0;
		for (n = 0; n < maxjobs; n++) joblist[n].pids = NULL;
	}
	else {
		n = -1;
		for (i = 0; i < maxjobs; i++) {
			if (!(joblist[i].pids)) {
				if (n < 0) n = i;
				continue;
			}
			for (j = 0; j <= joblist[i].npipe; j++)
				if (joblist[i].pids[j] == pid) break;
			if (j <= joblist[i].npipe) break;
			else if (joblist[i].pids[0] == childpgrp) {
				j = joblist[i].npipe + 1;
				break;
			}
		}
		if (i < maxjobs) /*EMPTY*/;
		else if (n >= 0) i = n;
		else {
			joblist = (jobtable *)realloc2(joblist,
				(maxjobs + BUFUNIT) * sizeof(jobtable));
			maxjobs += BUFUNIT;
			for (n = i; n < maxjobs; n++) joblist[n].pids = NULL;
		}
	}

	if (!(joblist[i].pids)) {
		joblist[i].pids = (p_id_t *)malloc2(BUFUNIT * sizeof(p_id_t));
		joblist[i].stats = (int *)malloc2(BUFUNIT * sizeof(int));
		joblist[i].npipe = 0;
		joblist[i].trp = NULL;
		joblist[i].tty = NULL;
# ifdef	USESGTTY
		joblist[i].ttyflag = NULL;
# endif
		j = 0;
	}
	else if (j > joblist[i].npipe) {
		if (!(j % BUFUNIT)) {
			joblist[i].pids = (p_id_t *)realloc2(joblist[i].pids,
				(j + BUFUNIT) * sizeof(p_id_t));
			joblist[i].stats = (int *)realloc2(joblist[i].stats,
				(j + BUFUNIT) * sizeof(int));
		}
		joblist[i].npipe = j;
	}

	joblist[i].pids[j] = pid;
	joblist[i].stats[j] = sig;
	if (!j && !(joblist[i].trp) && trp) {
#ifdef	MINIMUMSHELL
		joblist[i].trp = duplstree(trp, NULL);
#else
		joblist[i].trp = duplstree(trp, NULL, 0L);
#endif
		if (!isoppipe(joblist[i].trp) && joblist[i].trp -> next) {
			freestree(joblist[i].trp -> next);
			joblist[i].trp -> next = NULL;
		}
	}

#ifdef	JOBVERBOSE
	fprintf2(ttyout, "stackjob: %id: %id, %d:", mypid, pid, i);
	for (j = 0; j <= joblist[i].npipe; j++)
		fprintf2(ttyout, "%id ", joblist[i].pids[j]);
	fputnl(ttyout);
#endif	/* JOBVERBOSE */
	return(i);
}

int stoppedjob(pid)
p_id_t pid;
{
	int i, j, sig;

	if (stopped) return(1);
	if ((i = searchjob(pid, &j)) >= 0) {
		for (; j <= joblist[i].npipe; j++) {
			sig = joblist[i].stats[j];
			if (sig > 0 && sig < 128) return(1);
			else if (!sig) return(0);
		}
	}
	return(-1);
}

VOID checkjob(verbose)
int verbose;
{
	int i, j;

	while (waitjob(-1, NULL, WNOHANG | WUNTRACED) > 0);
	if (verbose) for (i = 0; i < maxjobs; i++) {
		if (!(joblist[i].pids)) continue;
		j = joblist[i].npipe;
		if (joblist[i].stats[j] >= 0 && joblist[i].stats[j] < 128)
			continue;

		if (joblist[i].trp
		&& (isopbg(joblist[i].trp) || isopnown(joblist[i].trp)))
			joblist[i].trp -> type = OP_NONE;
		if (jobok && interactive && !nottyout) dispjob(i, stderr);
		free(joblist[i].pids);
		free(joblist[i].stats);
		if (joblist[i].trp) {
			freestree(joblist[i].trp);
			free(joblist[i].trp);
		}
		if (joblist[i].tty) free(joblist[i].tty);
# ifdef	USESGTTY
		if (joblist[i].ttyflag) free(joblist[i].ttyflag);
# endif
		joblist[i].pids = NULL;
	}
}

VOID killjob(VOID_A)
{
	int i, j;

	for (i = 0; i < maxjobs; i++) {
		if (!(joblist[i].pids)) continue;
		j = joblist[i].stats[joblist[i].npipe];
		if (j < 0 || j >= 128) continue;
		Xkillpg(joblist[i].pids[0], SIGHUP);
		if (j > 0) Xkillpg(joblist[i].pids[0], SIGCONT);
	}
}
#endif	/* !NOJOB */

#ifndef	NOALIAS
static shaliastable *NEAR duplalias(alias)
shaliastable *alias;
{
	shaliastable *dupl;
	int i, n;

	if (!alias) n = 0;
	else for (n = 0; alias[n].ident; n++);
	dupl = (shaliastable *)malloc2((n + 1) * sizeof(shaliastable));
	for (i = 0; i < n; i++) {
		dupl[i].ident = strdup2(alias[i].ident);
		dupl[i].comm = strdup2(alias[i].comm);
	}
	dupl[i].ident = NULL;
	return(dupl);
}

VOID freealias(alias)
shaliastable *alias;
{
	int i;

	if (alias) {
		for (i = 0; alias[i].ident; i++) {
			free(alias[i].ident);
			free(alias[i].comm);
		}
		free(alias);
	}
}

static int cmpalias(vp1, vp2)
CONST VOID_P vp1;
CONST VOID_P vp2;
{
	shaliastable *ap1, *ap2;

	ap1 = (shaliastable *)vp1;
	ap2 = (shaliastable *)vp2;
	return(strpathcmp2(ap1 -> ident, ap2 -> ident));
}

int checkalias(trp, ident, len, delim)
syntaxtree *trp;
char *ident;
int len, delim;
{
	int i;

	if (!trp || (trp -> flags & ST_NODE) || hascomm(trp)
	|| !interactive
	|| ((i = getstatid(parentstree(trp))) >= 0
	&& !(statementlist[i].type & STT_NEEDLIST))) return(-1);

	if ((!strchr(IFS_SET, delim) && !strchr(ALIASDELIMIT, delim)))
		return(-1);
	for (i = 0; shellalias[i].ident; i++)
		if (!(shellalias[i].flags & AL_USED)
		&& !strncommcmp(ident, shellalias[i].ident, len)
		&& !(shellalias[i].ident[len])) break;
	if (!(shellalias[i].ident)) return(-1);

	*ident = '\0';
	shellalias[i].flags |= AL_USED;
	return(i);
}
#endif	/* NOALIAS */

static int NEAR _evalexpression(s, ptr, resultp)
char *s;
int ptr;
long *resultp;
{
	long n;
	int i, c, base;

	while (s[ptr] && strchr(IFS_SET, s[ptr])) ptr++;
	if (!s[ptr]) return(-1);

	if (s[ptr] == '+') {
		if ((ptr = _evalexpression(s, ptr + 1, resultp)) < 0)
			return(-1);
	}
	else if (s[ptr] == '-') {
		if ((ptr = _evalexpression(s, ptr + 1, resultp)) < 0)
			return(-1);
		*resultp = -(*resultp);
	}
	else if (s[ptr] == '~') {
		if ((ptr = _evalexpression(s, ptr + 1, resultp)) < 0)
			return(-1);
		*resultp = ~(*resultp);
	}
	else if (s[ptr] == '!' && s[ptr + 1] != '=') {
		if ((ptr = _evalexpression(s, ptr + 1, resultp)) < 0)
			return(-1);
		*resultp = (*resultp) ? 0L : 1L;
	}
	else if (s[ptr] == '(') {
		if ((ptr = evalexpression(s, ptr + 1, resultp, 9)) < 0)
			return(-1);
		while (strchr(IFS_SET, s[ptr])) ptr++;
		if (s[ptr++] != ')') return(-1);
	}
	else if (isdigit2(s[ptr])) {
		if (s[ptr] != '0') base = 10;
		else if (toupper2(s[++ptr]) != 'X') base = 8;
		else {
			base = 16;
			ptr++;
		}

		n = 0;
		for (i = ptr; s[i]; i++) {
			c = s[i];
			if (isdigit2(c)) c -= '0';
			else if (islower2(c)) c -= 'a' - 10;
			else if (isupper2(c)) c -= 'A' - 10;
			else c = -1;
			if (c < 0 || c >= base) break;

			if (n > MAXTYPE(long) / base
			|| (n == MAXTYPE(long) / base
			&& c > MAXTYPE(long) % base))
				return(-1);
			n = n * base + c;
		}
		if (i <= ptr) return(-1);
		ptr = i;
		*resultp = n;
	}
	else return(-1);

	while (s[ptr] && strchr(IFS_SET, s[ptr])) ptr++;
	return(ptr);
}

static int NEAR evalexpression(s, ptr, resultp, lvl)
char *s;
int ptr;
long *resultp;
int lvl;
{
	long n;

	if (lvl < 1) {
		if ((ptr = _evalexpression(s, ptr, resultp)) < 0) return(-1);
	}
	else {
		if ((ptr = evalexpression(s, ptr, resultp, lvl - 1)) < 0)
			return(-1);
	}

	while (s[ptr]) {
		if (lvl < 1) {
			if (s[ptr] == '*') {
				ptr = _evalexpression(s, ptr + 1, &n);
				if (ptr < 0) return(-1);
				*resultp *= n;
			}
			else if (s[ptr] == '/') {
				ptr = _evalexpression(s, ptr + 1, &n);
				if (ptr < 0 || !n) return(-1);
				*resultp /= n;
			}
			else if (s[ptr] == '%') {
				ptr = _evalexpression(s, ptr + 1, &n);
				if (ptr < 0 || !n) return(-1);
				*resultp %= n;
			}
			else break;
		}
		else if (lvl < 2) {
			if (s[ptr] == '+') {
				ptr = evalexpression(s, ptr + 1, &n, lvl - 1);
				if (ptr < 0) return(-1);
				*resultp += n;
			}
			else if (s[ptr] == '-') {
				ptr = evalexpression(s, ptr + 1, &n, lvl - 1);
				if (ptr < 0) return(-1);
				*resultp -= n;
			}
			else break;
		}
		else if (lvl < 3) {
			if (s[ptr] == '<' && s[ptr + 1] == '<') {
				ptr = evalexpression(s, ptr + 2, &n, lvl - 1);
				if (ptr < 0) return(-1);
				*resultp <<= n;
			}
			else if (s[ptr] == '>' && s[ptr + 1] == '>') {
				ptr = evalexpression(s, ptr + 2, &n, lvl - 1);
				if (ptr < 0) return(-1);
				*resultp >>= n;
			}
			else break;
		}
		else if (lvl < 4) {
			if ((s[ptr] == '<' && s[ptr + 1] == '=')
			|| (s[ptr] == '=' && s[ptr + 1] == '<')) {
				ptr = evalexpression(s, ptr + 2, &n, lvl - 1);
				if (ptr < 0) return(-1);
				*resultp = (long)(*resultp <= n);
			}
			else if ((s[ptr] == '>' && s[ptr + 1] == '=')
			|| (s[ptr] == '=' && s[ptr + 1] == '>')) {
				ptr = evalexpression(s, ptr + 2, &n, lvl - 1);
				if (ptr < 0) return(-1);
				*resultp = (long)(*resultp >= n);
			}
			else if (s[ptr] == '>') {
				ptr = evalexpression(s, ptr + 1, &n, lvl - 1);
				if (ptr < 0) return(-1);
				*resultp = (long)(*resultp > n);
			}
			else if (s[ptr] == '<') {
				ptr = evalexpression(s, ptr + 1, &n, lvl - 1);
				if (ptr < 0) return(-1);
				*resultp = (long)(*resultp < n);
			}
			else break;
		}
		else if (lvl < 5) {
			if (s[ptr] == '=' && s[ptr + 1] == '=') {
				ptr = evalexpression(s, ptr + 2, &n, lvl - 1);
				if (ptr < 0) return(-1);
				*resultp = (long)(*resultp == n);
			}
			else if (s[ptr] == '!' && s[ptr + 1] == '=') {
				ptr = evalexpression(s, ptr + 2, &n, lvl - 1);
				if (ptr < 0) return(-1);
				*resultp = (long)(*resultp != n);
			}
			else break;
		}
		else if (lvl < 6) {
			if (s[ptr] == '&' && s[ptr + 1] != '&') {
				ptr = evalexpression(s, ptr + 1, &n, lvl - 1);
				if (ptr < 0) return(-1);
				*resultp = (*resultp & n);
			}
			else break;
		}
		else if (lvl < 7) {
			if (s[ptr] == '^') {
				ptr = evalexpression(s, ptr + 1, &n, lvl - 1);
				if (ptr < 0) return(-1);
				*resultp = (*resultp ^ n);
			}
			else break;
		}
		else if (lvl < 8) {
			if (s[ptr] == '|' && s[ptr + 1] != '|') {
				ptr = evalexpression(s, ptr + 1, &n, lvl - 1);
				if (ptr < 0) return(-1);
				*resultp = (*resultp | n);
			}
			else break;
		}
		else if (lvl < 9) {
			if (s[ptr] == '&' && s[ptr + 1] == '&') {
				ptr = evalexpression(s, ptr + 2, &n, lvl - 1);
				if (ptr < 0) return(-1);
				*resultp = (long)(*resultp && n);
			}
			else break;
		}
		else {
			if (s[ptr] == '|' && s[ptr + 1] == '|') {
				ptr = evalexpression(s, ptr + 2, &n, lvl - 1);
				if (ptr < 0) return(-1);
				*resultp = (long)(*resultp || n);
			}
			else break;
		}
	}

	return(ptr);
}

char *evalposixsubst(s, ptrp)
char *s;
int *ptrp;
{
	char *cp;
	long n;
	int i, len;

	i = 1;
	if (s[i] == '(') i++;

	s += i;
	len = *ptrp - i * 2;
	s = strndup2(s, len);
	if (i <= 1) {
#ifndef	BASHBUG
	/* bash cannot include 'case' statement within $() */
		syntaxtree *trp, *stree;
		redirectlist red;

		red.fd = red.type = red.new = 0;
		red.filename = "";
		i = len = 0;
		stree = newstree(NULL);
		trp = startvar(stree, &red, "", &i, &len, 0);
		trp -> cont = CN_COMM;
		trp = analyze(s, trp, -1);
		if (trp && (trp -> cont & CN_SBST) != CN_CASE) trp = NULL;
		freestree(stree);
		free(stree);
		if (trp) cp = NULL;
		else
#endif	/* !BASHBUG */
		if (!(cp = evalbackquote(s))) *ptrp = -1;
	}
	else {
		n = 0L;
		if (!(cp = evalvararg(s, '\'', EA_BACKQ, 0))) *ptrp = -1;
		else {
			for (i = 0; cp[i] && strchr(IFS_SET, cp[i]); i++);
			if (cp[i]) {
				i = evalexpression(cp, i, &n, 9);
				if (i < 0 || cp[i]) *ptrp = -1;
			}
			free(cp);
		}

		if (*ptrp < 0) cp = NULL;
		else if (asprintf2(&cp, "%ld", n) < 0) error("malloc()");
	}
	free(s);

	return(cp);
}

#if	!MSDOS
static VOID NEAR addmailpath(path, msg, mtime)
char *path, *msg;
time_t mtime;
{
	int i;

	for (i = 0; i < maxmailpath; i++)
		if (!strpathcmp(path, mailpathlist[i].path)) break;
	if (i < maxmailpath) {
		if (mailpathlist[i].msg) free(mailpathlist[i].msg);
	}
	else {
		mailpathlist = (mailpath_t *)realloc2(mailpathlist,
			(i + 1) * sizeof(mailpath_t));
		maxmailpath = i + 1;
		mailpathlist[i].path = strdup2(path);
	}
	mailpathlist[i].msg = msg;
	mailpathlist[i].mtime = mtime;
}

VOID replacemailpath(s, multi)
char *s;
int multi;
{
	mailpath_t *mailpath;
	time_t mtime;
	char *cp, *msg, path[MAXPATHLEN];
	int i, max, len;

	mailpath = mailpathlist;
	max = maxmailpath;
	mailpathlist = NULL;
	maxmailpath = 0;

	if (!multi) {
		for (i = 0; i < max; i++)
			if (!strpathcmp(s, mailpath[i].path)) break;
		mtime = (i < max) ? mailpath[i].mtime : 0;
		addmailpath(s, NULL, mtime);
	}
	else while (*s) {
		if ((cp = strchr(s, ':'))) len = (cp++) - s;
		else {
			len = strlen(s);
			cp = s + len;
		}
		if ((msg = strchr(s, '%'))) {
			if (msg > s + len) msg = NULL;
			else {
				i = s + len - (msg + 1);
				len = msg - s;
				msg = strndup2(&(s[len + 1]), i);
			}
		}
		strncpy2(path, s, len);
		s = cp;
		for (i = 0; i < max; i++)
			if (!strpathcmp(path, mailpath[i].path)) break;
		mtime = (i < max) ? mailpath[i].mtime : 0;
		addmailpath(path, msg, mtime);
	}

	for (i = 0; i < max; i++) {
		free(mailpath[i].path);
		if (mailpath[i].msg) free(mailpath[i].msg);
	}
	if (mailpath) free(mailpath);
}

VOID checkmail(reset)
int reset;
{
	static time_t lastchecked = 0;
	time_t now;
	int i;

	if (reset) {
		for (i = 0; i < maxmailpath; i++) {
			free(mailpathlist[i].path);
			if (mailpathlist[i].msg) free(mailpathlist[i].msg);
		}
		free(mailpathlist);
		mailpathlist = NULL;
		maxmailpath = 0;
		return;
	}

	if (mailcheck > 0) {
		now = time2();
		if (now < lastchecked + mailcheck) return;
		lastchecked = now;
	}

	for (i = 0; i < maxmailpath; i++)
		cmpmail(mailpathlist[i].path,
			mailpathlist[i].msg, &(mailpathlist[i].mtime));
}
#endif	/* !MSDOS */

#ifndef	NOJOB
/*ARGSUSED*/
int posixjobs(trp)
syntaxtree *trp;
{
	int i, j;

	if (mypid != orgpgrp) return(RET_SUCCESS);
	checkjob(0);
	for (i = 0; i < maxjobs; i++) {
		if (!(joblist[i].pids)) continue;
		j = joblist[i].npipe;

		if (joblist[i].stats[j] >= 0 && joblist[i].stats[j] < 128)
			dispjob(i, stdout);
		else {
			if (isopbg(joblist[i].trp) || isopnown(joblist[i].trp))
				joblist[i].trp -> type = OP_NONE;
			dispjob(i, stdout);
			free(joblist[i].pids);
			free(joblist[i].stats);
			if (joblist[i].trp) {
				freestree(joblist[i].trp);
				free(joblist[i].trp);
			}
			joblist[i].pids = NULL;
		}
	}
	return(RET_SUCCESS);
}

/*ARGSUSED*/
int posixfg(trp)
syntaxtree *trp;
{
	termioctl_t tty;
	char *s;
	int i, j, n, ret;

	s = ((trp -> comm) -> argc > 1) ? (trp -> comm) -> argv[1] : NULL;
	checkjob(0);
	if ((i = getjob(s)) < 0) {
		execerror((trp -> comm) -> argv[1], ER_NOSUCHJOB, 0);
		return(RET_FAIL);
	}
	n = joblist[i].npipe;
	if (tioctl(ttyio, REQGETP, &tty) < 0) {
		doperror((trp -> comm) -> argv[0], "fatal error");
		prepareexit(-1);
		Xexit(RET_FATALERR);
	}
	fprintf2(stderr, "[%d] %id", i + 1, joblist[i].pids[n]);
	fputnl(stderr);
	if (joblist[i].tty) tioctl(ttyio, REQSETP, joblist[i].tty);
# ifdef	USESGTTY
	if (joblist[i].ttyflag) ioctl(ttyio, TIOCLSET, joblist[i].ttyflag);
# endif
	gettermio(joblist[i].pids[0]);
	if ((j = joblist[i].stats[n]) > 0 && j < 128) {
		Xkillpg(joblist[i].pids[0], SIGCONT);
		for (j = 0; j <= n; j++) joblist[i].stats[j] = 0;
	}
	if (isopbg(joblist[i].trp) || isopnown(joblist[i].trp))
		joblist[i].trp -> type = OP_NONE;
	ret = waitchild(joblist[i].pids[n], NULL);
	gettermio(orgpgrp);
	if (tioctl(ttyio, REQSETP, &tty) < 0) {
		doperror((trp -> comm) -> argv[0], "fatal error");
		prepareexit(-1);
		Xexit(RET_FATALERR);
	}
	return(ret);
}

/*ARGSUSED*/
int posixbg(trp)
syntaxtree *trp;
{
	char *s;
	int i, j, n;

	s = ((trp -> comm) -> argc > 1) ? (trp -> comm) -> argv[1] : NULL;
	checkjob(0);
	if ((i = getjob(s)) < 0) {
		execerror((trp -> comm) -> argv[1], ER_NOSUCHJOB, 0);
		return(RET_FAIL);
	}
	n = joblist[i].npipe;
	if ((j = joblist[i].stats[n]) > 0 && j < 128) {
		Xkillpg(joblist[i].pids[0], SIGCONT);
		for (j = 0; j <= n; j++) joblist[i].stats[j] = 0;
	}
	if (!isopbg(joblist[i].trp) || !isopnown(joblist[i].trp))
		joblist[i].trp -> type = OP_BG;
	if (interactive && !nottyout) dispjob(i, stderr);
	prevjob = lastjob;
	lastjob = i;
	return(RET_SUCCESS);
}
#endif	/* !NOJOB */

#ifndef	NOALIAS
int posixalias(trp)
syntaxtree *trp;
{
	shaliastable *alias;
	char **argv;
	int i, n, len, set, ret;

	argv = (trp -> comm) -> argv;
	if ((trp -> comm) -> argc <= 1) {
		alias = duplalias(shellalias);
		for (i = 0; alias[i].ident; i++);
		if (i > 1) qsort(alias, i, sizeof(shaliastable), cmpalias);
		for (i = 0; alias[i].ident; i++) {
			fprintf2(stdout, "alias %k='%k'",
				alias[i].ident, alias[i].comm);
			fputnl(stdout);
		}
		freealias(alias);
		return(RET_SUCCESS);
	}

	ret = RET_SUCCESS;
	for (n = 1; n < (trp -> comm) -> argc; n++) {
		set = 0;
		for (len = 0; argv[n][len]; len++) if (argv[n][len] == '=') {
			set = 1;
			break;
		}
		for (i = 0; shellalias[i].ident; i++)
			if (!strncommcmp(shellalias[i].ident, argv[n], len)
			&& !(shellalias[i].ident[len]))
				break;

		if (!set) {
			if (!(shellalias[i].ident)) {
				if (interactive) {
					fputs("alias: ", stderr);
					execerror(argv[n], ER_NOTFOUND, 0);
				}
				ret = RET_FAIL;
				ERRBREAK;
			}
			fprintf2(stdout, "alias %k='%k'",
				shellalias[i].ident, shellalias[i].comm);
			fputnl(stdout);
		}
		else if (shellalias[i].ident) {
			free(shellalias[i].comm);
			shellalias[i].comm = strdup2(&(argv[n][++len]));
		}
		else {
			shellalias = (shaliastable *)realloc2(shellalias,
				(i + 2) * sizeof(shaliastable));
			shellalias[i].ident = strndup2(argv[n], len);
			shellalias[i].comm = strdup2(&(argv[n][++len]));
			shellalias[++i].ident = NULL;
		}
	}
	return(ret);
}

int posixunalias(trp)
syntaxtree *trp;
{
	reg_t *re;
	char **argv;
	int i, n, c, ret;

	argv = (trp -> comm) -> argv;
	ret = RET_SUCCESS;
	for (n = 1; n < (trp -> comm) -> argc; n++) {
		c = 0;
		re = regexp_init(argv[n], -1);
		for (i = 0; shellalias[i].ident; i++) {
			if (re) {
				if (!regexp_exec(re, shellalias[i].ident, 0))
					continue;
			}
			else if (strcommcmp(shellalias[i].ident, argv[n]))
				continue;
			c++;
			free(shellalias[i].ident);
			free(shellalias[i].comm);
			for (; shellalias[i + 1].ident; i++) {
				shellalias[i].ident = shellalias[i + 1].ident;
				shellalias[i].comm = shellalias[i + 1].comm;
			}
			shellalias[i].ident = NULL;
		}
		if (re) regexp_free(re);
		if (!c) {
			if (interactive) {
				fputs("alias: ", stderr);
				execerror(argv[n], ER_NOTALIAS, 0);
			}
			ret = RET_FAIL;
			ERRBREAK;
		}
	}
	return(ret);
}
#endif	/* NOALIAS */

int posixkill(trp)
syntaxtree *trp;
{
	char *s;
	p_id_t pid;
	int i, n, sig;

	if ((trp -> comm) -> argc <= 1) {
		fputs("usage: kill [ -sig ] pid ...\n", stderr);
		fputs("for a list of signals: kill -l", stderr);
		fputnl(stderr);
		return(RET_SYNTAXERR);
	}
#ifdef	SIGTERM
	sig = SIGTERM;
#else
	sig = 0;
#endif
	s = (trp -> comm) -> argv[1];
	if (s[0] != '-') i = 1;
	else {
		if (s[1] == 'l') {
			for (sig = n = 0; sig < NSIG; sig++) {
				for (i = 0; signallist[i].sig >= 0; i++)
					if (sig == signallist[i].sig) break;
				if (signallist[i].sig < 0) continue;
				fprintf2(stdout, "%s%c",
					signallist[i].ident,
					(++n % 16) ? ' ' : '\n');
			}
			if (n % 16) fputnl(stdout);
			fflush(stdout);
			return(RET_SUCCESS);
		}
		if ((sig = isnumeric(&(s[1]))) < 0) {
			for (i = 0; signallist[i].sig >= 0; i++)
				if (!strcmp(&(s[1]), signallist[i].ident))
					break;
			if (signallist[i].sig < 0) {
				execerror(&(s[1]), ER_UNKNOWNSIG, 0);
				return(RET_FAIL);
			}
			sig = signallist[i].sig;
		}
		if (sig < 0 || sig >= NSIG) {
			execerror(s, ER_NUMOUTRANGE, 0);
			return(RET_FAIL);
		}
		i = 2;
	}

#ifndef	NOJOB
	checkjob(0);
#endif
	for (; i < (trp -> comm) -> argc; i++) {
		s = (trp -> comm) -> argv[i];
#ifndef	NOJOB
		if (*s == '%') {
			if ((n = getjob(s)) < 0) {
				execerror(s, ER_NOSUCHJOB, 0);
				return(RET_FAIL);
			}
			n = Xkillpg(joblist[n].pids[0], sig);
		}
		else
#endif	/* !NOJOB */
		if ((pid = isnumeric(s)) < (p_id_t)0) {
			fputs("usage: kill [ -sig ] pid ...\n", stderr);
			fputs("for a list of signals: kill -l", stderr);
			fputnl(stderr);
			return(RET_SYNTAXERR);
		}
#if	MSDOS
		else if (pid && pid != mypid) {
			errno = EPERM;
			n = -1;
		}
#endif
		else n = kill(pid, sig);

		if (n < 0) {
			doperror((trp -> comm) -> argv[0], s);
			return(RET_FAIL);
		}
	}
	return(RET_SUCCESS);
}

static int NEAR testsub1(c, s, ptrp)
int c;
char *s;
int *ptrp;
{
	struct stat st;
	int ret;

	ret = -1;
	switch (c) {
		case 'r':
			if (s) ret = (Xaccess(s, R_OK) >= 0)
				? RET_SUCCESS : RET_FAIL;
			break;
		case 'w':
			if (s) ret = (Xaccess(s, W_OK) >= 0)
				? RET_SUCCESS : RET_FAIL;
			break;
		case 'x':
			if (s) ret = (Xaccess(s, X_OK) >= 0)
				? RET_SUCCESS : RET_FAIL;
			break;
		case 'f':
			if (s) ret = (*s && Xstat(s, &st) >= 0
			&& (st.st_mode & S_IFMT) != S_IFDIR)
				? RET_SUCCESS : RET_FAIL;
			break;
		case 'd':
			if (s) ret = (*s && Xstat(s, &st) >= 0
			&& (st.st_mode & S_IFMT) == S_IFDIR)
				? RET_SUCCESS : RET_FAIL;
			break;
		case 'c':
			if (s) ret = (*s && Xstat(s, &st) >= 0
			&& (st.st_mode & S_IFMT) == S_IFCHR)
				? RET_SUCCESS : RET_FAIL;
			break;
		case 'b':
			if (s) ret = (*s && Xstat(s, &st) >= 0
			&& (st.st_mode & S_IFMT) == S_IFBLK)
				? RET_SUCCESS : RET_FAIL;
			break;
		case 'p':
			if (s) ret = (*s && Xstat(s, &st) >= 0
			&& (st.st_mode & S_IFMT) == S_IFIFO)
				? RET_SUCCESS : RET_FAIL;
			break;
		case 'u':
			if (s) ret = (*s && Xstat(s, &st) >= 0
			&& st.st_mode & S_ISUID)
				? RET_SUCCESS : RET_FAIL;
			break;
		case 'g':
			if (s) ret = (*s && Xstat(s, &st) >= 0
			&& st.st_mode & S_ISGID)
				? RET_SUCCESS : RET_FAIL;
			break;
		case 'k':
			if (s) ret = (*s && Xstat(s, &st) >= 0
			&& st.st_mode & S_ISVTX)
				? RET_SUCCESS : RET_FAIL;
			break;
#if	defined (BASHSTYLE) || defined (STRICTPOSIX)
	/* "test" on bash & POSIX has -e & -L option */
		case 'e':
			if (s) ret = (*s && Xlstat(s, &st) >= 0)
				? RET_SUCCESS : RET_FAIL;
			break;
		case 'L':
#endif
		case 'h':
			if (s) ret = (*s && Xlstat(s, &st) >= 0
			&& (st.st_mode & S_IFMT) == S_IFLNK)
				? RET_SUCCESS : RET_FAIL;
			break;
		case 's':
			if (s) ret = (*s && Xstat(s, &st) >= 0
			&& st.st_size > 0)
				? RET_SUCCESS : RET_FAIL;
			break;
		case 't':
			if (s) {
#ifdef	BASHBUG
	/* Maybe bash's bug. */
				if ((ret = isnumeric(s)) < 0) ret = 1;
#else
				if ((ret = isnumeric(s)) < 0) ret = 0;
#endif
				ret = (isatty(ret)) ? RET_SUCCESS : RET_FAIL;
			}
			else {
				(*ptrp)++;
#ifdef	BASHBUG
	/* Maybe bash's bug. */
				ret = 0;
#else
				ret = 1;
#endif
				return((isatty(ret)) ? RET_SUCCESS : RET_FAIL);
			}
			break;
		case 'z':
			if (s) ret = (!*s) ? RET_SUCCESS : RET_FAIL;
			break;
		case 'n':
			if (s) ret = (*s) ? RET_SUCCESS : RET_FAIL;
			break;
		default:
			ret = -2;
			break;
	}
	if (ret >= 0) *ptrp += 2;
	return(ret);
}

static int NEAR testsub2(argc, argv, ptrp)
int argc;
char *argv[];
int *ptrp;
{
	char *s, *a1, *a2;
	int ret, v1, v2;

	if (*ptrp >= argc) return(-1);
	if (argv[*ptrp][0] == '(' && !argv[*ptrp][1]) {
		(*ptrp)++;
		if ((ret = testsub3(argc, argv, ptrp, 0)) < 0)
			return(ret);
		if (*ptrp >= argc
		|| argv[*ptrp][0] != ')' || argv[*ptrp][1]) return(-1);
		(*ptrp)++;
		return(ret);
	}
	if (*ptrp + 2 < argc) {
		s = argv[*ptrp + 1];
		a1 = argv[*ptrp];
		a2 = argv[*ptrp + 2];
		ret = -1;

		if (s[0] == '!' && s[1] == '=' && !s[2])
			ret = (strcmp(a1, a2)) ? RET_SUCCESS : RET_FAIL;
		else if (s[0] == '=' && !s[1])
			ret = (!strcmp(a1, a2)) ? RET_SUCCESS : RET_FAIL;
		else if (s[0] == '-') {
			if ((v1 = isnumeric(a1)) < 0) {
#ifdef	BASHSTYLE
	/* bash's "test" does not allow arithmetic comparison with strings */
				return(-3);
#else
				v1 = 0;
#endif
			}
			if ((v2 = isnumeric(a2)) < 0) {
#ifdef	BASHSTYLE
	/* bash's "test" does not allow arithmetic comparison with strings */
				*ptrp += 2;
				return(-3);
#else
				v2 = 0;
#endif
			}
			if (s[1] == 'e' && s[2] == 'q' && !s[3])
				ret = (v1 == v2) ? RET_SUCCESS : RET_FAIL;
			else if (s[1] == 'n' && s[2] == 'e' && !s[3])
				ret = (v1 != v2) ? RET_SUCCESS : RET_FAIL;
			else if (s[1] == 'g' && s[2] == 't' && !s[3])
				ret = (v1 > v2) ? RET_SUCCESS : RET_FAIL;
			else if (s[1] == 'g' && s[2] == 'e' && !s[3])
				ret = (v1 >= v2) ? RET_SUCCESS : RET_FAIL;
			else if (s[1] == 'l' && s[2] == 't' && !s[3])
				ret = (v1 < v2) ? RET_SUCCESS : RET_FAIL;
			else if (s[1] == 'l' && s[2] == 'e' && !s[3])
				ret = (v1 <= v2) ? RET_SUCCESS : RET_FAIL;
			else {
				(*ptrp)++;
				return(-2);
			}
		}
		if (ret >= 0) {
			*ptrp += 3;
			return(ret);
		}
	}
	if (argv[*ptrp][0] == '-' && !argv[*ptrp][2]) {
		a1 = (*ptrp + 1 < argc) ? argv[*ptrp + 1] : NULL;
		ret = testsub1(argv[*ptrp][1], a1, ptrp);
		if (ret >= -1) return(ret);
	}
	ret = (argv[*ptrp][0]) ? RET_SUCCESS : RET_FAIL;
	(*ptrp)++;
	return(ret);
}

static int NEAR testsub3(argc, argv, ptrp, lvl)
int argc;
char *argv[];
int *ptrp, lvl;
{
	int ret1, ret2;

	if (lvl > 2) {
		if (*ptrp >= argc) return(-1);
		if (argv[*ptrp][0] == '!' && !argv[*ptrp][1]) {
			(*ptrp)++;
			if ((ret1 = testsub2(argc, argv, ptrp)) < 0)
				return(ret1);
			return(1 - ret1);
		}
		return(testsub2(argc, argv, ptrp));
	}
	if ((ret1 = testsub3(argc, argv, ptrp, lvl + 1)) < 0) return(ret1);
	if (*ptrp >= argc || argv[*ptrp][0] != '-'
	|| argv[*ptrp][1] != ((lvl) ? 'a' : 'o') || argv[*ptrp][2])
		return(ret1);

	(*ptrp)++;
	if ((ret2 = testsub3(argc, argv, ptrp, lvl)) < 0) return(ret2);
	ret1 = ((lvl)
		? (ret1 != RET_FAIL && ret2 != RET_FAIL)
		: (ret1 != RET_FAIL || ret2 != RET_FAIL))
			? RET_SUCCESS : RET_FAIL;
	return(ret1);
}

int posixtest(trp)
syntaxtree *trp;
{
	char **argv;
	int ret, ptr, argc;

	argc = (trp -> comm) -> argc;
	argv = (trp -> comm) -> argv;

	if (argc <= 0) return(RET_FAIL);
	if (argv[0][0] == '[' && !argv[0][1]
	&& (--argc <= 0 || argv[argc][0] != ']' || argv[argc][1])) {
		fputs("] missing", stderr);
		fputnl(stderr);
		return(RET_NOTICE);
	}
	if (argc <= 1) return(RET_FAIL);
	ptr = 1;
	ret = testsub3(argc, argv, &ptr, 0);
	if (!ret && ptr < argc) ret = -1;
	if (ret < 0) {
		switch (ret) {
			case -1:
				fputs("argument expected", stderr);
				break;
			case -2:
				fprintf2(stderr, "unknown operator %k",
					argv[ptr]);
				break;
			default:
				fputs("syntax error", stderr);
				break;
		}
		fputnl(stderr);
		return(RET_NOTICE);
	}
	return(ret);
}

#ifndef	NOPOSIXUTIL
int posixcommand(trp)
syntaxtree *trp;
{
	hashlist *hp;
	char *cp, **argv, *path;
	int i, n, flag, ret, id, type, argc;

	argc = (trp -> comm) -> argc;
	argv = (trp -> comm) -> argv;

	if ((flag = tinygetopt(trp, "pvV", &n)) < 0) return(RET_FAIL);

	if (interrupted) return(RET_INTR);
	if (argc <= n) return(ret_status);

# ifdef	BASHSTYLE
	ret = RET_FAIL;
# else
	ret = RET_SUCCESS;
# endif
	if (flag == 'V') {
		for (i = n; i < argc; i++) {
			if (typeone(argv[i], stdout) >= 0) {
# ifdef	BASHSTYLE
				ret = RET_SUCCESS;
# endif
			}
		}
		return(ret);
	}
	else if (flag == 'v') {
		for (i = n; i < argc; i++) {
#ifdef	NOALIAS
			type = checktype(argv[i], &id, 0);
#else
			type = checktype(argv[i], &id, 0, 0);
#endif

			cp = argv[i];
			if (type == CT_COMMAND) {
				type = searchhash(&hp, cp, NULL);
# ifdef	_NOUSEHASH
				if (!(type & CM_FULLPATH)) cp = (char *)hp;
# else
				if (type & CM_HASH) cp = hp -> path;
# endif
				if ((type & CM_NOTFOUND)
				|| Xaccess(cp, X_OK) < 0) {
					execerror(cp, ER_COMNOFOUND, 0);
					cp = NULL;
				}
			}

			if (cp) {
				kanjifputs(cp, stdout);
				fputnl(stdout);
# ifdef	BASHSTYLE
				ret = RET_SUCCESS;
# endif
			}
		}
		return(ret);
	}

#ifdef	NOALIAS
	type = checktype(argv[n], &id, 0);
#else
	type = checktype(argv[n], &id, 0, 0);
#endif
	if (verboseexec) {
		fprintf2(stderr, "+ %k", argv[n]);
		for (i = n + 1; i < argc; i++)
			fprintf2(stderr, " %k", argv[i]);
		fputnl(stderr);
	}

	(trp -> comm) -> argc -= n;
	(trp -> comm) -> argv += n;
	if (flag != 'p')
#if	MSDOS
		ret = exec_simplecom(trp, type, id);
#else
		ret = exec_simplecom(trp, type, id, 0);
#endif
	else {
		path = strdup2(getconstvar("PATH"));
		setenv2("PATH", DEFPATH, 1);
#if	MSDOS
		ret = exec_simplecom(trp, type, id);
#else
		ret = exec_simplecom(trp, type, id, 0);
#endif
		setenv2("PATH", path, 1);
		if (path) free(path);
	}
	(trp -> comm) -> argc = argc;
	(trp -> comm) -> argv = argv;
	return(ret);
}

int posixgetopts(trp)
syntaxtree *trp;
{
	static int ptr = 0;
	char *cp, *optstr, *posixoptarg, *name, **argv, buf[MAXLONGWIDTH + 1];
	int n, ret, quiet, argc;

	argc = (trp -> comm) -> argc;
	argv = (trp -> comm) -> argv;

	if (argc <= 2) {
		execerror(NULL, ER_MISSARG, 0);
		return(RET_FAIL);
	}
	if (identcheck(argv[2], '\0') <= 0) {
		execerror(argv[2], ER_NOTIDENT, 0);
		return(RET_FAIL);
	}

	optstr = argv[1];
	name = argv[2];
	quiet = 0;
	if (*optstr == ':') {
		quiet = 1;
		optstr++;
	}

	if (argc > 3) {
		argv = &(argv[2]);
		argc -= 2;
	}
	else {
		argv = argvar;
		argc = countvar(argv);
	}

	if (posixoptind <= 0) {
		ptr = 0;
		posixoptind = 1;
	}

	ret = RET_SUCCESS;
	posixoptarg = NULL;
	if (posixoptind >= argc || !argv[posixoptind]) ret = RET_FAIL;
	else if (ptr >= strlen(argv[posixoptind])) {
		if (++posixoptind < argc && argv[posixoptind]) ptr = 0;
		else ret = RET_FAIL;
	}

	if (ret == RET_SUCCESS && !ptr) {
		if (argv[posixoptind][0] != '-') ret = RET_FAIL;
		else if (argv[posixoptind][1] == '-'
		&& !(argv[posixoptind][2])) {
			posixoptind++;
			ret = RET_FAIL;
		}
		else if (!(argv[posixoptind][ptr + 1])) ret = RET_FAIL;
		else ptr++;
	}

	if (ret != RET_SUCCESS) n = '?';
	else {
		n = argv[posixoptind][ptr++];
		if (n == ':' || ismsb(n) || !(cp = strchr2(optstr, n))) {
			buf[0] = n;
			buf[1] = '\0';
			n = '?';
			if (quiet) posixoptarg = buf;
			else execerror(buf, ER_BADOPTIONS, 2);
		}
		else if (*(cp + 1) == ':') {
			if (argv[posixoptind][ptr])
				posixoptarg = &(argv[posixoptind++][ptr]);
			else if (++posixoptind < argc && argv[posixoptind])
				posixoptarg = argv[posixoptind++];
			else {
				buf[0] = n;
				buf[1] = '\0';
				if (quiet) {
					n = ':';
					posixoptarg = buf;
				}
				else {
					n = '?';
					execerror(buf, ER_MISSARG, 2);
				}
			}
			ptr = 0;
		}
	}

	if (posixoptarg) setenv2("OPTARG", posixoptarg, 0);
	else if (ret == RET_SUCCESS) unset("OPTARG", sizeof("OPTARG") - 1);

	buf[0] = n;
	buf[1] = '\0';
	setenv2(name, buf, 0);

	n = posixoptind;
	snprintf2(buf, sizeof(buf), "%d", n);
	setenv2("OPTIND", buf, 0);
	posixoptind = n;

	return(ret);
}
#endif	/* NOPOSIXUTIL */
#endif	/* !MINIMUMSHELL && (!FD || (FD >= 2 && !_NOORIGSHELL)) */
