/*
** popen.c
** Written by Rick Schaeffer (ricks@isc-br.isc-br.com)
NAME
     popen, pclose - initiate I/O to/from a process

SYNOPSIS
     #include <stdio.h>

     FILE *popen(command, type)
     char *command, *type;

     pclose(stream)
     FILE *stream;

DESCRIPTION
     The arguments to popen are pointers to null-terminated
     strings containing respectively a command line and an
     I/O mode, either "r" for reading or "w" for writing.  It
     creates a pipe between the calling process and the command
     to be executed.  The value returned is a stream pointer that
     can be used (as appropriate) to write to the standard input
     of the command or read from its standard output.

     A stream opened by popen **MUST** be closed by pclose, which
     waits for the associated process to terminate and returns
     the exit status of the command.

     Because stdio files are shared, a type "r" command may be
     used as an input filter, and a type "w" as an output filter.

DIAGNOSTICS
     Popen returns a null pointer if files or processes cannot be
     created.

     Pclose returns -1 if stream is not associated with a
     `popened' command.

*/

#include <stdio.h>
#include <string.h>
#include <exec/types.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <proto/dos.h>
#include <proto/exec.h>
#include <arp/arpbase.h>
#include <ios1.h>

struct ArpBase						*ArpBase;
static struct Process				*thistask;
static struct ProcessControlBlock	pcb;

struct pstruct {
	FILE	*fptr;
	struct ZombieMsg	exitmsg;
	};

struct pstruct poarray[6];

FILE *popen(cmd,mode)
char	*cmd;
char	*mode;
{
	char 		*parms;
	static char tempname[] = "pipe:pXXX.XXX";
	char 		*pname,redir[20],*mktemp();
	short		i;
	int			pmode;
	struct pstruct	*poptr;
	BPTR		pfd;

	if (thistask == NULL)
		thistask = (struct Process *) FindTask(NULL);
	if (ArpBase == NULL)
		ArpBase = (struct ArpBase *) OpenLibrary(ArpName,ArpVersion);
	if (ArpBase == NULL) {
		fprintf(stderr,"Arp Open Failed\n");
		return(NULL);
		}
	poptr = NULL;
	for (i=0; i<6; i++) {
		if (poarray[i].fptr == NULL) {
			poptr = &poarray[i];
			break;
			}
		}
	if (poptr == NULL) {
		fprintf(stderr,"popen: Unable to find an open pipe\n");
		return(NULL);
		}
	if (strcmp(mode,"r") == 0)
		pmode = MODE_NEWFILE;
	else if (strcmp(mode,"w") == 0)
		pmode = MODE_OLDFILE;
	else {
		fprintf(stderr,"popen: Mode must be 'r' or 'w'\n");
		return(NULL);
		}
	tempname[5] = 'a' + i;
	strcpy(redir,tempname);
	pname = mktemp(redir);				/* set up a pipe: file name */

	setmem(&pcb, sizeof(struct ProcessControlBlock), 0);
	/* Now get the child's stack and priority set up */
	if (thistask->pr_CLI) {
		struct CommandLineInterface		*cli;
		cli = (struct CommandLineInterface *) BADDR(thistask->pr_CLI);
		pcb.pcb_StackSize = cli->cli_DefaultStack << 2;
		}
	else
		pcb.pcb_StackSize = thistask->pr_StackSize;
	pcb.pcb_Pri = thistask->pr_Task.tc_Node.ln_Pri;
	/* Open the side of the pipe for the child */
	pfd = Open(pname,pmode);
	if (pfd == 0) {
		fprintf(stderr,"popen: Unable to open pipe file\n");
		return(NULL);
		}
	if (pmode == MODE_NEWFILE)
		pcb.pcb_Output = pfd;
	else
		pcb.pcb_Input = pfd;

	/* Locate the break between command and parameters */
	parms = strpbrk(cmd," \t");
	if (parms) {
		*parms++ = 0;
		parms = stpblk(parms);
		if (parms && *parms == 0)
			parms = NULL;
		}
	/* Create a port for the child's exit message */
	poptr->exitmsg.zm_ExecMessage.mn_ReplyPort = CreatePort(NULL,0);
	if (poptr->exitmsg.zm_ExecMessage.mn_ReplyPort == 0) {
		fprintf(stderr,"popen: Couldn't create message port\n");
		return(NULL);
		}
	pcb.pcb_LastGasp = &poptr->exitmsg;

	if (ASyncRun(cmd,parms,&pcb) < 0) {
		fprintf(stderr,"popen: AsyncRun failed\n");
		DeletePort(poptr->exitmsg.zm_ExecMessage.mn_ReplyPort);
		return(NULL);
		}
	/* Now open our side of the pipe */
	poptr->fptr = fopen(pname,mode);
	if (poptr->fptr == NULL) {
		fprintf(stderr,"popen: Unable to open pipe file %s\n",pname);
		DeletePort(poptr->exitmsg.zm_ExecMessage.mn_ReplyPort);
		return(NULL);
		}
	return(poptr->fptr);
}

pclose(fptr)
FILE	*fptr;
{
	short		i;

	for (i=0; i<6; i++)
		if (poarray[i].fptr == fptr)
			break;
	if (i > 5) {
		fprintf(stderr,"popen: DISASTER...couldn't find file pointer in pclose\n");
		exit(1);
		}
	fclose(fptr);
	WaitPort(poarray[i].exitmsg.zm_ExecMessage.mn_ReplyPort);
	poarray[i].fptr = NULL;
	DeletePort(poarray[i].exitmsg.zm_ExecMessage.mn_ReplyPort);
	return(poarray[i].exitmsg.zm_ReturnCode);
}

char *mktemp(template)
char *template;
{
	register char *cp;
	register unsigned long val;

	cp = template;
	cp += strlen(cp);
	for (val = (unsigned long) FindTask(0L) ; ; )
		if (*--cp == 'X') {
			*cp = val%10 + '0';
			val /= 10;
		} else if (*cp != '.')
			break;

	if (*++cp != 0) {
		*cp = 'A';
		while (access(template, 0) == 0) {
			if (*cp == 'Z') {
				*template = 0;
				break;
			}
			++*cp;
		}
	} else {
		if (access(template, 0) == 0)
			*template = 0;
	}
	return template;
}
