/*
 *	stream.c
 *
 *	stream I/O functions
 */

#include "headers.h"
#include "depend.h"
#include "typesize.h"
#include "malloc.h"
#include "sysemu.h"
#include "pathname.h"
#include "termio.h"
#include "stream.h"

#ifdef	DEP_STREAMSOCKET
#include "socket.h"
#endif
#if	defined (FD) && !defined (DEP_ORIGSHELL)
#include "string.h"
#include "pathname.h"
#include "wait.h"
#include "dosdisk.h"
#include "unixdisk.h"
#endif

#define	PIPEDIR			"PIPE_DIR"
#define	PIPEFILE		"FAKEPIPE"
#ifdef	USECRNL
#define	ISCRNL(fp)		(!((fp) -> status & XS_BINARY) \
				|| ((fp) -> flags & XF_CRNL))
#else
#define	ISCRNL(fp)		((fp) -> flags & XF_CRNL)
#endif

#if	defined (FD) && !defined (DEP_ORIGSHELL)
# ifdef	USESIGACTION
extern sigcst_t signal2 __P_((int, sigcst_t));
# else
#define	signal2			signal
# endif
# ifdef	DEP_DOSLFN
extern char *preparefile __P_((CONST char *, char *));
# endif
#endif	/* FD && !DEP_ORIGSHELL */

#ifdef	FD
extern char *Xstrchr __P_((CONST char *, int));
#endif
#ifdef	DEP_DOSDRIVE
extern int dosflushbuf __P_((int));
#endif
#if	defined (FD) && !defined (DEP_ORIGSHELL)
extern int mktmpdir __P_((char *));
extern int rmtmpdir __P_((CONST char *));
#endif

#if	defined (FD) && !defined (DEP_ORIGSHELL)
extern char *deftmpdir;
extern char *tmpfilename;
#endif

#ifdef	DEP_ORIGSTREAM
static int NEAR mode2flags __P_((CONST char *, int *));
# ifdef	DEP_STREAMLOG
static XFILE *NEAR fmalloc __P_((int, int, int, int, CONST char *));
# else
static XFILE *NEAR fmalloc __P_((int, int, int));
# endif
static int NEAR checkfp __P_((XFILE *, int));
# ifdef	DEP_STREAMLOG
static VOID NEAR dumplog __P_((CONST char *, ALLOC_T, XFILE *));
# endif
static int NEAR fillbuf __P_((XFILE *));
static int NEAR flushbuf __P_((XFILE *));
# ifdef	DEP_STREAMSOCKET
static int NEAR replytelnet __P_((XFILE *));
# endif
#endif	/* DEP_ORIGSTREAM */
#if	!defined (FD) && !defined (DEP_ORIGSTREAM)
static char *NEAR fgets2 __P_((int));
#endif

#ifdef	DEP_ORIGSTREAM
static XFILE stdiobuf[] = {
	{STDIN_FILENO, XS_RDONLY, 0},
	{STDOUT_FILENO, XS_WRONLY, XF_LINEBUF},
	{STDERR_FILENO, XS_WRONLY, XF_NOBUF},
};
#define	STDIOBUFSIZ		arraysize(stdiobuf)
#endif	/* DEP_ORIGSTREAM */
#if	defined (FD) && !defined (DEP_ORIGSHELL)
# if	!MSDOS
static p_id_t popenpid = (p_id_t)-1;
# endif
static int popenstat = 0;
#endif	/* FD && !DEP_ORIGSHELL */

#ifdef	DEP_ORIGSTREAM
int (*stream_isnfsfunc)__P_((CONST char *)) = NULL;
XFILE *Xstdin = &(stdiobuf[0]);
XFILE *Xstdout = &(stdiobuf[1]);
XFILE *Xstderr = &(stdiobuf[2]);
#endif


#ifdef	DEP_ORIGSTREAM
static int NEAR mode2flags(mode, flagsp)
CONST char *mode;
int *flagsp;
{
	int flags;

	if (!mode) {
		errno = EINVAL;
		return(-1);
	}
	flags = 0;
	if (flagsp) flags |= (*flagsp & ~O_ACCMODE);

	switch (*(mode++)) {
		case 'r':
			flags |= O_RDONLY;
			break;
		case 'w':
			flags |= (O_WRONLY | O_CREAT | O_TRUNC);
			break;
		case 'a':
			flags |= (O_WRONLY | O_APPEND | O_CREAT);
			break;
		default:
			errno = EINVAL;
			return(-1);
/*NOTREACHED*/
			break;
	}
	if (*mode == '+') {
		flags &= ~O_ACCMODE;
		flags |= O_RDWR;
		mode++;
	}
	flags |= O_BINARY;
	if (flagsp) *flagsp = flags;

	return((*mode == 'b') ? 1 : 0);
}

# ifdef	DEP_STREAMLOG
static XFILE *NEAR fmalloc(fd, flags, isbin, timeout, path)
int fd, flags, isbin, timeout;
CONST char *path;
# else
static XFILE *NEAR fmalloc(fd, flags, isbin)
int fd, flags, isbin;
# endif
{
	XFILE *fp;
	ALLOC_T len;

	len = sizeof(XFILE);
# ifdef	DEP_STREAMLOG
	if (!path) path = nullstr;
	len += strlen(path);
# endif
	fp = (XFILE *)Xmalloc(len);

	fp -> fd = fd;
# ifdef	DEP_STREAMTIMEOUT
#  ifdef	DEP_STREAMLOG
	fp -> timeout = timeout;
#  else
	fp -> timeout = -1;
#  endif
# endif	/* DEP_STREAMTIMEOUT */
	fp -> ptr = fp -> count = (ALLOC_T)0;
	fp -> status = fp -> flags = 0;
# ifdef	DEP_STREAMLOG
	fp -> dumpfunc = NULL;
	fp -> debuglvl = 0;
	fp -> debugmes = NULL;
	Xstrcpy(fp -> path, path);
# endif

	switch (flags & O_ACCMODE) {
		case O_RDONLY:
			fp -> status |= XS_RDONLY;
			break;
		case O_WRONLY:
			fp -> status |= XS_WRONLY;
			break;
		default:
			break;
	}
	if (isbin) fp -> status |= XS_BINARY;

	return(fp);
}

XFILE *Xfopen(path, mode)
CONST char *path, *mode;
{
# ifdef	DEP_STREAMLOCK
#  ifndef	NOFTRUNCATE
	int trunc;
#  endif
	int isnfs, operation;
# endif	/* DEP_STREAMLOCK */
	XFILE *fp;
	int fd, flags, isbin;

	flags = 0;
	if ((isbin = mode2flags(mode, &flags)) < 0) return(NULL);
# ifdef	DEP_STREAMLOCK
	isnfs = (stream_isnfsfunc) ? (*stream_isnfsfunc)(path) : 1;
#  ifndef	NOFTRUNCATE
	if (isnfs) trunc = 0;
	else {
		trunc = (flags & O_TRUNC);
		flags &= ~O_TRUNC;
	}
#  endif
# endif	/* DEP_STREAMLOCK */
	fd = Xopen(path, flags, 0666);
	if (fd < 0) return(NULL);

# ifdef	DEP_STREAMLOG
	fp = fmalloc(fd, flags, isbin, -1, path);
# else
	fp = fmalloc(fd, flags, isbin);
# endif
	if (!fp) {
		VOID_C Xclose(fd);
		return(NULL);
	}

# ifdef	DEP_STREAMLOCK
	if (!isnfs) {
		operation = ((flags & O_ACCMODE) == O_RDONLY)
			? LOCK_SH : LOCK_EX;
		if (Xflock(fd, operation | LOCK_NB) < 0) {
			VOID_C Xfclose(fp);
			return(NULL);
		}
		fp -> status |= XS_LOCKED;
#  ifndef	NOFTRUNCATE
		if (trunc && Xftruncate(fd, (off_t)0) < 0) {
			VOID_C Xfclose(fp);
			return(NULL);
		}
#  endif
	}
# endif	/* DEP_STREAMLOCK */

	return(fp);
}

XFILE *Xfdopen(fd, mode)
int fd;
CONST char *mode;
{
# if	!MSDOS
	int n;
# endif
	XFILE *fp;
	int flags, isbin;

	flags = 0;
	if ((isbin = mode2flags(mode, &flags)) < 0) return(NULL);
# if	!MSDOS
	n = fcntl(fd, F_GETFL);
	if (n < 0) return(NULL);
	if ((flags & O_APPEND) != (n & O_APPEND)) {
		errno = EINVAL;
		return(NULL);
	}
	switch (n & O_ACCMODE) {
		case O_RDONLY:
			if ((flags & O_ACCMODE) != O_RDONLY) {
				errno = EINVAL;
				return(NULL);
			}
			break;
		case O_WRONLY:
			if ((flags & O_ACCMODE) != O_WRONLY) {
				errno = EINVAL;
				return(NULL);
			}
			break;
		default:
			break;
	}
# endif	/* !MSDOS */

# ifdef	DEP_STREAMLOG
	fp = fmalloc(fd, flags, isbin, -1, NULL);
# else
	fp = fmalloc(fd, flags, isbin);
# endif
	if (!fp) return(NULL);

	return(fp);
}

int Xfclose(fp)
XFILE *fp;
{
	int i, duperrno;

	if (!fp) {
		errno = EINVAL;
		return(EOF);
	}
	if (fp -> status & XS_CLOSED) return(0);

	duperrno = errno;
	if (flushbuf(fp) < 0) return(EOF);

# ifdef	DEP_STREAMLOCK
	if (fp -> status & XS_LOCKED) {
		VOID_C Xlseek(fp -> fd, (off_t)0, L_XTND);
		VOID_C Xflock(fp -> fd, LOCK_UN);
	}
# endif	/* DEP_STREAMLOCK */
	if (!(fp -> flags & XF_NOCLOSE)) {
# ifdef	DEP_STREAMSOCKET
		if (fp -> flags & XF_CONNECTED) shutdown(fp -> fd, SHUT_RDWR);
# endif
		if (Xclose(fp -> fd) < 0) return(EOF);
	}
	for (i = 0; i < STDIOBUFSIZ; i++) if (fp == &(stdiobuf[i])) break;
	if (i >= STDIOBUFSIZ) Xfree(fp);
	else {
		fp -> status |= XS_CLOSED;
		fp -> status &= ~(XS_EOF | XS_ERROR | XS_READ | XS_WRITTEN);
		fp -> ptr = fp -> count = (ALLOC_T)0;
	}
	errno = duperrno;

	return(0);
}

static int NEAR checkfp(fp, status)
XFILE *fp;
int status;
{
	if (!fp) {
		errno = EINVAL;
		return(-1);
	}
	if ((status & XS_ERROR) && (fp -> status & XS_ERROR)) return(-1);
	if ((status & XS_RDONLY) && (fp -> status & XS_WRONLY)) {
		errno = EBADF;
		fp -> status |= XS_ERROR;
		return(-1);
	}
	if ((status & XS_WRONLY) && (fp -> status & XS_RDONLY)) {
		errno = EBADF;
		fp -> status |= XS_ERROR;
		return(-1);
	}

	return(0);
}

VOID Xclearerr(fp)
XFILE *fp;
{
	if (checkfp(fp, 0) < 0) return;
	fp -> status &= ~(XS_EOF | XS_ERROR);
}

int Xfeof(fp)
XFILE *fp;
{
	if (checkfp(fp, 0) < 0) return(-1);

	return(fp -> status & (XS_EOF | XS_CLOSED));
}

int Xferror(fp)
XFILE *fp;
{
	if (checkfp(fp, 0) < 0) return(-1);

	return(fp -> status & XS_ERROR);
}

int Xfileno(fp)
XFILE *fp;
{
	if (checkfp(fp, 0) < 0) return(-1);

	return(fp -> fd);
}

VOID Xsetflags(fp, flags)
XFILE *fp;
int flags;
{
	if (checkfp(fp, 0) < 0) return;
	fp -> flags |= flags;
}

# ifdef	DEP_STREAMTIMEOUT
VOID Xsettimeout(fp, timeout)
XFILE *fp;
int timeout;
{
	if (checkfp(fp, 0) < 0) return;
	fp -> timeout = timeout;
}
# endif	/* DEP_STREAMTIMEOUT */

# ifdef	DEP_STREAMLOG
static VOID NEAR dumplog(buf, size, fp)
CONST char *buf;
ALLOC_T size;
XFILE *fp;
{
	if (!(fp -> dumpfunc) || !(fp -> debuglvl)) return;
	if (debuglevel < fp -> debuglvl - 1) return;

	(*(fp -> dumpfunc))((CONST u_char *)buf, size, fp -> debugmes);
}
# endif	/* DEP_STREAMLOG */

static int NEAR fillbuf(fp)
XFILE *fp;
{
	ALLOC_T len;
	int n;

	if (checkfp(fp, XS_ERROR | XS_RDONLY) < 0) return(-1);
	if (fp == Xstdin) {
		if (Xstdout -> flags & XF_LINEBUF) VOID_C flushbuf(Xstdout);
		if (Xstderr -> flags & XF_LINEBUF) VOID_C flushbuf(Xstderr);
	}

# ifdef	DEP_STREAMTIMEOUT
	if (fp -> status & XS_NOAHEAD) {
#  ifdef	DEP_STREAMLOG
		if (fp -> status & XS_CLEARBUF) {
			fp -> status &= ~XS_CLEARBUF;
			fp -> ptr = (ALLOC_T)0;
		}
		else
#  endif
		if (fp -> ptr >= XF_BUFSIZ) {
#  ifdef	DEP_STREAMLOG
			dumplog(fp -> buf, XF_BUFSIZ, fp);
#  endif
			fp -> ptr = (ALLOC_T)0;
		}
		n = checkread(fp -> fd, &(fp -> buf[fp -> ptr]),
			(ALLOC_T)1, fp -> timeout);
	}
	else
# endif	/* DEP_STREAMTIMEOUT */
	{
		fp -> ptr = (ALLOC_T)0;
		len = (fp -> flags & XF_NOBUF) ? (ALLOC_T)1 : XF_BUFSIZ;
		n = sureread(fp -> fd, fp -> buf, len);
	}
	if (n <= 0) {
		if (n < 0) fp -> status |= XS_ERROR;
		else {
			fp -> status |= XS_EOF;
			fp -> status &= ~XS_READ;
		}
		fp -> count = (ALLOC_T)0;
		return(-1);
	}

	fp -> count = (ALLOC_T)n;
	fp -> status |= XS_READ;
# ifdef	DEP_STREAMLOG
#  ifdef	DEP_STREAMTIMEOUT
	if (fp -> status & XS_NOAHEAD) {
		if (fp -> buf[fp -> ptr] == '\n') {
			fp -> status |= XS_CLEARBUF;
			dumplog(fp -> buf, fp -> ptr + 1, fp);
		}
	}
	else
#  endif
	dumplog(fp -> buf, (ALLOC_T)n, fp);
# endif	/* DEP_STREAMLOG */

	return(0);
}

static int NEAR flushbuf(fp)
XFILE *fp;
{
	ALLOC_T len;
	int n;

	if (checkfp(fp, XS_ERROR) < 0) return(-1);
	fp -> status &= ~XS_WRITTEN;
	if (fp -> status & XS_RDONLY) return(0);

	len = fp -> ptr;
	fp -> ptr = fp -> count = (ALLOC_T)0;
	if (!len || (fp -> status & XS_CLOSED)) return(0);

	n = surewrite(fp -> fd, fp -> buf, len);
	if (n < 0) {
		fp -> status |= XS_ERROR;
		return(-1);
	}
# ifdef	DEP_DOSDRIVE
	if (chkopenfd(fp -> fd) == DEV_DOS && dosflushbuf(fp -> fd) < 0) {
		fp -> status |= XS_ERROR;
		return(-1);
	}
# endif
# ifdef	DEP_STREAMLOG
	dumplog(fp -> buf, (ALLOC_T)n, fp);
# endif

	return(n);
}

int Xfflush(fp)
XFILE *fp;
{
	return((flushbuf(fp) < 0) ? EOF : 0);
}

int Xfpurge(fp)
XFILE *fp;
{
	if (checkfp(fp, XS_RDONLY) < 0) return(EOF);

	fp -> status &= ~(XS_EOF | XS_READ);
	fp -> ptr = fp -> count = (ALLOC_T)0;

	return(0);
}

int Xfread(buf, size, fp)
char *buf;
ALLOC_T size;
XFILE *fp;
{
	ALLOC_T ptr, len;
	int nl, total;

	if (checkfp(fp, XS_ERROR | XS_RDONLY) < 0) return(-1);
	if ((fp -> status & XS_WRITTEN) && flushbuf(fp) < 0) return(-1);
	if (!(fp -> status & XS_READ)) fp -> ptr = fp -> count = (ALLOC_T)0;
	nl = total = 0;

	while (size > (ALLOC_T)0) {
		if (fp -> status & (XS_EOF | XS_CLOSED)) break;
		if (!(fp -> count) && fillbuf(fp) < 0) break;
		if (nl && fp -> buf[fp -> ptr] != '\n') {
			*(buf++) = '\r';
			size--;
			total++;
		}
		len = fp -> count;
		if (len > size) len = size;

		nl = 0;
		if (ISCRNL(fp)) for (ptr = (ALLOC_T)0; ptr < len; ptr++) {
# ifdef	USECRNL
			if (fp -> buf[fp -> ptr + ptr] == CH_EOF) {
				fp -> status |= XS_EOF;
				break;
			}
# endif
			if (fp -> buf[fp -> ptr + ptr] == '\r') {
				nl++;
				len = ptr;
				break;
			}
		}

		memcpy(buf, &(fp -> buf[fp -> ptr]), len);
		fp -> ptr += len;
		buf += len;
		size -= len;
		fp -> count -= len;
		total += len;

# ifdef	USECRNL
		if (fp -> status & XS_EOF)
			fp -> ptr = fp -> count = (ALLOC_T)0;
		else
# endif
		if (nl) {
			(fp -> ptr)++;
			(fp -> count)--;
		}
	}

	return((fp -> status & XS_ERROR) ? -1 : total);
}

int Xfwrite(buf, size, fp)
CONST char *buf;
ALLOC_T size;
XFILE *fp;
{
	ALLOC_T ptr, len, max;
	int n, nl, prev, total;

	if (checkfp(fp, XS_ERROR | XS_WRONLY) < 0) return(-1);
	if (fp -> status & XS_READ) {
		fp -> status &= ~(XS_EOF | XS_READ);
		fp -> ptr = fp -> count = (ALLOC_T)0;
	}
	if (fp -> status & XS_CLOSED) return(0);
	max = (fp -> flags & XF_NOBUF) ? (ALLOC_T)1 : XF_BUFSIZ;
	total = 0;
	prev = '\0';

	while (size > (ALLOC_T)0) {
		len = max - fp -> count;
		if (len > size) len = size;

		nl = 0;
		if (ISCRNL(fp)) for (ptr = (ALLOC_T)0; ptr < len; ptr++) {
			if (buf[ptr] == '\n' && prev != '\r') {
				nl++;
				len = ptr;
				break;
			}
			prev = buf[ptr];
		}

		memcpy(&(fp -> buf[fp -> ptr]), buf, len);
		if (nl) fp -> buf[fp -> ptr + len++] = '\r';
		total += len;
		fp -> ptr += len;
		buf += len;
		size -= len;
		fp -> count += len;
		fp -> status |= XS_WRITTEN;
		if (fp -> count >= max && flushbuf(fp) < 0) break;

		if (nl) {
			fp -> buf[(fp -> ptr)++] = '\n';
			(fp -> count)++;
			fp -> status |= XS_WRITTEN;
			if (fp -> count >= max && flushbuf(fp) < 0) break;
		}
	}

	if (fp -> status & XS_ERROR) return(total);
	if (!(fp -> flags & XF_LINEBUF)) return(total);

	for (len = fp -> ptr; len > (ALLOC_T)0; len--)
		if (fp -> buf[len - 1] == '\n') break;
	if (len <= (ALLOC_T)0) return(total);
	n = surewrite(fp -> fd, fp -> buf, len);
	if (n < 0) {
		fp -> status |= XS_ERROR;
		total -= (int)len - n;
		return(total);
	}
	fp -> ptr -= len;
	memmove(fp -> buf, &(fp -> buf[len]), fp -> ptr);

	return(total);
}

int Xfgetc(fp)
XFILE *fp;
{
	u_char uc;
	int n;

# ifdef	DEP_STREAMTIMEOUT
	if (fp -> flags & XF_NONBLOCK) fp -> status |= XS_NOAHEAD;
# endif
	n = Xfread((char *)&uc, sizeof(uc), fp);
# ifdef	DEP_STREAMTIMEOUT
	fp -> status &= ~XS_NOAHEAD;
# endif

	return((n > 0) ? (int)uc : EOF);
}

int Xfputc(c, fp)
int c;
XFILE *fp;
{
	u_char uc;

	uc = (u_char)c;
	if (Xfwrite((char *)&uc, sizeof(uc), fp) < (int)sizeof(uc))
		return(EOF);

	return(c);
}

# ifdef	DEP_STREAMSOCKET
static int NEAR replytelnet(fp)
XFILE *fp;
{
	u_char buf[3];
	int c, s;

	s = Xfileno(fp);
	if (issocket(s) < 0) return(0);

	c = Xfgetc(fp);
	switch (c) {
		case TELNET_WILL:
		case TELNET_WONT:
			c = Xfgetc(fp);
			buf[0] = TELNET_IAC;
			buf[1] = TELNET_DONT;
			buf[2] = c;
			VOID_C Xwrite(s, (char *)buf, sizeof(buf));
			break;
		case TELNET_DO:
		case TELNET_DONT:
			c = Xfgetc(fp);
			buf[0] = TELNET_IAC;
			buf[1] = TELNET_WONT;
			buf[2] = c;
			VOID_C Xwrite(s, (char *)buf, sizeof(buf));
			break;
		case TELNET_IAC:
			return(c);
/*NOTREACHED*/
			break;
		default:
			break;
	}

	return(0);
}
# endif	/* DEP_STREAMSOCKET */

char *Xfgets(fp)
XFILE *fp;
{
# ifdef	DEP_STREAMLOG
	int dupdebuglvl;
# endif
	char *buf;
	ALLOC_T len, size;
	int n, c;

	if (checkfp(fp, XS_ERROR | XS_RDONLY) < 0) return(NULL);
	buf = c_realloc(NULL, 0, &size);

	len = (ALLOC_T)0;
	for (;;) {
# ifdef	DEP_STREAMLOG
		dupdebuglvl = fp -> debuglvl;
		if (fp -> flags & XF_NONBLOCK) fp -> debuglvl = 0;
# endif
		n = c = Xfgetc(fp);
# ifdef	DEP_STREAMLOG
		fp -> debuglvl = dupdebuglvl;
# endif
		if (c == EOF) {
			if (!len || (fp -> status & XS_ERROR)) /*EMPTY*/;
			else break;
			Xfree(buf);
			return(NULL);
		}
# ifdef	DEP_STREAMSOCKET
		if ((fp -> flags & XF_TELNET) && c == TELNET_IAC) {
			c = replytelnet(fp);
			if (!c) continue;
		}
# endif
		buf = c_realloc(buf, len, &size);
# ifdef	FD
		if ((fp -> flags & XF_NULLCONV) && !c) c = '\n';
# endif

		buf[len++] = c;
		if (n == '\n') break;
	}

	if (len > 0 && buf[len - 1] == '\n') len--;
	buf[len++] = '\0';
	buf = Xrealloc(buf, len);

	return(buf);
}

int Xfputs(s, fp)
CONST char *s;
XFILE *fp;
{
# ifdef	FD
	CONST char *eol;
# endif
	ALLOC_T len;

# ifdef	FD
	if (fp -> flags & XF_NULLCONV) while ((eol = Xstrchr(s, '\n'))) {
		len = eol++ - s;
		if (Xfwrite(s, len, fp) < (int)len || Xfputc('\0', fp) == EOF)
			return(EOF);
		s = eol;
	}
# endif
	len = strlen(s);

	return((Xfwrite(s, len, fp) < (int)len) ? EOF : 0);
}

VOID Xsetbuf(fp)
XFILE *fp;
{
	if (fp) {
		fp -> flags |= XF_NOBUF;
		fp -> flags &= ~XF_LINEBUF;
		fp -> ptr = fp -> count = (ALLOC_T)0;
	}
}

VOID Xsetlinebuf(fp)
XFILE *fp;
{
	if (fp && (fp == Xstdout || fp == Xstderr)) {
		VOID_C flushbuf(fp);
		fp -> flags |= XF_LINEBUF;
		fp -> flags &= ~XF_NOBUF;
	}
}
#endif	/* DEP_ORIGSTREAM */

#if	defined (FD) && !defined (DEP_ORIGSHELL)
XFILE *Xpopen(command, mode)
CONST char *command, *mode;
{
# if	!MSDOS
	extern char **environ;
	int fds[2];
# endif
# ifdef	DEP_DOSLFN
	char *cp, tmp[MAXPATHLEN];
# endif
	char *cmdline, path[MAXPATHLEN];
	int l1, l2;

# if	!MSDOS
	if (pipe(fds) < 0) popenpid = (p_id_t)-1;
	else if ((popenpid = Xfork()) < (p_id_t)0) {
		safeclose(fds[0]);
		safeclose(fds[1]);
	}
	else if (popenpid) {
		safeclose(fds[1]);
		return(Xfdopen(fds[0], mode));
	}
	else {
		Xdup2(fds[1], STDOUT_FILENO);
		safeclose(fds[0]);
		safeclose(fds[1]);
		execle("/bin/sh", "sh", "-c", command, NULL, environ);
		_exit(1);
	}
# endif	/* !MSDOS */

	Xstrcpy(path, PIPEDIR);
	if (mktmpdir(path) < 0) return(NULL);
# ifdef	DEP_DOSLFN
	if (!(cp = preparefile(path, tmp))) return(NULL);
	else if (cp == tmp) Xstrcpy(path, tmp);
# endif
	Xstrcpy(strcatdelim(path), PIPEFILE);

	l1 = strlen(command);
	l2 = strlen(path);
	cmdline = Xmalloc(l1 + l2 + 3 + 1);
	Xstrncpy(cmdline, command, l1);
	Xstrcpy(&(cmdline[l1]), " > ");
	l1 += 3;
	Xstrcpy(&(cmdline[l1]), path);
	popenstat = system(cmdline);

	return(Xfopen(path, mode));
}

int Xpclose(fp)
XFILE *fp;
{
# if	!MSDOS
	sigcst_t ivec, qvec, hvec;
	wait_pid_t w;
	p_id_t pid;
# endif
	char *cp, path[MAXPATHLEN];
	int duperrno;

	duperrno = 0;
	if (Xfclose(fp)) duperrno = errno;

# if	!MSDOS
	if (popenpid >= (p_id_t)0) {
		if (!popenpid) popenstat = 0;
		else {
			ivec = signal2(SIGINT, SIG_IGN);
			qvec = signal2(SIGQUIT, SIG_IGN);
			hvec = signal2(SIGHUP, SIG_IGN);
			VOID_C kill(popenpid, SIGPIPE);
			for (;;) {
				pid = Xwait4(popenpid, &w, WUNTRACED, NULL);
				if (pid == popenpid) break;
				else if (pid < (p_id_t)0) {
					duperrno = errno;
					break;
				}
			}
			VOID_C signal2(SIGINT, ivec);
			VOID_C signal2(SIGQUIT, qvec);
			VOID_C signal2(SIGHUP, hvec);

			if (duperrno) popenstat = seterrno(duperrno);
			else if (WIFSTOPPED(w)) popenstat = 128 + WSTOPSIG(w);
			else if (WIFSIGNALED(w)) popenstat = 128 + WTERMSIG(w);
			else popenstat = WEXITSTATUS(w);
		}
		popenpid = (p_id_t)-1;
		return(popenstat);
	}
# endif	/* !MSDOS */

	if (!deftmpdir || !*deftmpdir || !tmpfilename || !*tmpfilename)
		return(seterrno(ENOENT));
	strcatdelim2(path, deftmpdir, tmpfilename);
	Xstrcpy(strcatdelim(path), PIPEDIR);
	Xstrcpy((cp = strcatdelim(path)), PIPEFILE);

	if (Xunlink(path) < 0) duperrno = errno;
	*(--cp) = '\0';
	if (rmtmpdir(path) < 0) duperrno = errno;
	if (duperrno) return(seterrno(duperrno));

	return(popenstat);
}
#endif	/* FD && !DEP_ORIGSHELL */

int fputnl(fp)
XFILE *fp;
{
	if (Xfputc('\n', fp) == EOF || Xfflush(fp) == EOF) return(-1);

	return(0);
}

VOID putc2(c)
int c;
{
	if (c) VOID_C Xfputc(c, Xstdout);
}

#ifndef	FD
# ifndef	DEP_ORIGSTREAM
static char *NEAR fgets2(fd)
int fd;
{
	char *buf;
	ALLOC_T len, size;
	u_char uc;
	int n;

	buf = c_realloc(NULL, 0, &size);

	len = (ALLOC_T)0;
	for (;;) {
		n = read(fd, &uc, sizeof(uc));
		if (n <= 0) {
			if (!len || n < 0) {
				Xfree(buf);
				return(NULL);
			}
			break;
		}
		if (uc == '\n') break;
		buf = c_realloc(buf, len, &size);
		buf[len++] = uc;
	}
	buf[len++] = '\0';

	return(Xrealloc(buf, len));
}
# endif	/* !DEP_ORIGSTREAM */

char *gets2(prompt)
CONST char *prompt;
{
# ifdef	DEP_ORIGSTREAM
#  ifdef	DEP_ORIGSHELL
	extern int ttyio;
	int fd;
#  endif
	XFILE *fp;
# endif	/* DEP_ORIGSTREAM */
	char *buf;

	errputs(prompt, 0);
	VOID_C Xfflush(Xstderr);

# ifdef	DEP_ORIGSTREAM
#  ifdef	DEP_ORIGSHELL
	fd = safe_dup(ttyio);
	fp = (fd >= 0) ? Xfdopen(fd, "r") : NULL;
	if (fp) /*EMPTY*/;
	else
#  endif
	fp = Xstdin;
	buf = Xfgets(fp);
	safefclose(fp);
# else	/* !DEP_ORIGSTREAM */
	buf = fgets2(STDIN_FILENO);
# endif	/* !DEP_ORIGSTREAM */

	return(buf);
}
#endif	/* !FD */

VOID puts2(s)
CONST char *s;
{
	if (s) VOID_C Xfputs(s, Xstdout);
}

VOID putnl(VOID_A)
{
	VOID_C fputnl(Xstdout);
}

VOID errputs(s, nl)
CONST char *s;
int nl;
{
	if (s) VOID_C Xfputs(s, Xstderr);
	if (nl) VOID_C fputnl(Xstderr);
}
