#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <dos.h>
#include <ios1.h>
#include <fcntl.h>
#include <libraries/dosextens.h>
#include <exec/lists.h>
#include <exec/nodes.h>
#include <rsbx/ChildTasking.h>


struct pstruct {
	struct MinNode node;
	FILE	*fptr1;
	struct ChildNode *child;
	};

static struct MinList PList = { 0 };


FILE *popenl(char *args, ...);
FILE *popenv(char **args, char *mode);
int pclose(FILE *fptr);
static char *mktemp(char *template);



/****** rsbx.lib/popenl() ****************************************************
*
*   NAME
*	popenl -- Initiate I/O to or from a command.
*
*   SYNOPSIS
*	pipe = popenl(arg0, ..., argn, NULL, mode)
*
*	FILE *popenl(char *, char *, ...);
*
*   FUNCTION
*	Creates a stream between the calling process and the stdin or stdout
*	    streams of the command it executes. Mode should be "r" to read
*	    the stdout stream of the command, or "w" to write to the stdin
*	    stream of the command. 
*
*   INPUTS
*	arg0          - Name of file to execute.
*       ...           - Other command line arguments needed by the file to be
*	                executed.
*	NULL          - Zero.
*	mode          _ I/O mode string.
*
*   RESULT
*	pipe          - File pointer to use to read or write to the command
*	                with.
*
*   NOTES
*	A stream opened by popenl() should be closed by pclose().
*
*   SEE ALSO
*	popenv(), pclose().
*
*   BUGS
*	This function does not create a complete CLI enviroment for the child
*	    process. Use of this function with a command that expects a BCPL
*	    environment will crash the system. The Unix function 'popen' takes
*	    as arguments a command string which it passes to the shell, and a
*	    mode string.
*
******************************************************************************
*
*/

FILE *popenl(char *args, ...)
	{
	char **nullarg;

	for(nullarg = (char **)(&args); *nullarg != 0; nullarg++);

	return(popenv(&args, *(nullarg+1)));
	}


/****** rsbx.lib/popenv() ****************************************************
*
*   NAME
*	popenv -- Initiate I/O to or from a command.
*
*   SYNOPSIS
*	pipe = popenv(argv, mode)
*
*	FILE *popenv(char **, char *);
*
*   FUNCTION
*	Creates a stream between the calling process and the stdin or stdout
*	    streams of the command it executes. Mode should be "r" to read
*	    the stdout stream of the command, or "w" to write to the stdin
*	    stream of the command. 
*
*   INPUTS
*	argv          - An array of pointer to the argument strings for the
*	                command. The last pointer must be followed by a 0
*	                pointer. The first pointer, argv[0], is the name of
*	                file to execute.
*	mode          _ I/O mode string.
*
*   RESULT
*	pipe          - File pointer to use to read or write to the command
*	                with.
*
*   NOTES
*	A stream opened by popenv() should be closed by pclose().
*
*   SEE ALSO
*	popenl(), pclose().
*
*   BUGS
*	This function does not create a complete CLI enviroment for the child
*	    process. Use of this function with a command that expects a BCPL
*	    environment will crash the system. The Unix function 'popen' takes
*	    as arguments a command string which it passes to the shell, and a
*	    mode string.
*
******************************************************************************
*
*/

FILE *popenv(char **args, char *mode)
	{
	struct LAUNCHENV env = { 0,0,0,0,0,0,0 };
	static char tempname[] = "pipe:popen.XXXX.XXXXXXXX";
	char *pname;
	char redir[64];
	struct pstruct *poptr;
	char *mode1;
	int mode2;
	int f2;

	if (!PList.mlh_Head)
		{
		NewList(&PList);
		}

	if ((strcmp(mode,"r") != 0) && (strcmp(mode,"w") != 0))
		{
		fprintf(stderr,"popen: bad mode!\n");
		return(NULL);
		}

	if (*mode == 'r')
		{
		mode1 = "r";
		mode2 = MODE_NEWFILE;
		}
	else
		{
		mode1 = "w";
		mode2 = MODE_OLDFILE;
		}

	strcpy(redir,tempname);

	if (!(poptr = (struct pstruct *)malloc(sizeof(struct pstruct))))
		{
		fprintf(stderr,"popen: Out of memory!\n");
		return(NULL);
		}

	if (!(pname = mktemp(redir)))
		{
		fprintf(stderr,"popen: Unable to find an available pipe.\n");
		free(poptr);
		return(NULL);
		}

	if (!(poptr->fptr1 = fopen(pname, mode1)))
		{
		fprintf(stderr,"popen: Unable to open pipe end 1 file %s mode %s\n", pname, m
de);
		free(poptr);
		return(NULL);
		}

	if (!(f2 = Open(pname, mode2)))
		{
		fprintf(stderr,"popen: Unable to open pipe end 2 file %s mode %s\n", pname, m
de);
		fclose(poptr->fptr1);
		free(poptr);
		return(NULL);
		}

	env.priority = ((struct Task *)FindTask(NULL))->tc_Node.ln_Pri;
	if (*mode == 'r')
		{
		env.std_out = f2;
		}
	else
		{
		env.std_in = f2;
		}
	env.le_fh1 = f2;

	if (!(poptr->child = LaunchChildv(&env, *args, args)))
		{
		fprintf(stderr,"popen: Fork failed\n");
		Close(f2);
		fclose(poptr->fptr1);
		free(poptr);
		return(NULL);
		}

	AddTail(&PList, poptr);

	return(poptr->fptr1);
	}


/****** rsbx.lib/pclose() ****************************************************
*
*   NAME
*	pclose -- Closes pipe opened by popenl() or by popenv().
*
*   SYNOPSIS
*	status = pclose(pipe)
*
*	int pclose(FILE *);
*
*   FUNCTION
*	Waits for the command associated with the pipe to exit, closes the
*	    stream, and returns the return code from the command.
*
*   INPUTS
*	pipe          - Stream pointer returned by a call to popenl() or to
*	                popenv().
*
*   RESULT
*	status        - The return code from the command executed, or -1 if
*	                pipe wasn't opened by popenl() or by popenv().
*
*   NOTES
*
*   SEE ALSO
*	popenl(), popenv().
*
*   BUGS
*	None known.
*
******************************************************************************
*
*/

int pclose(FILE *fptr)
	{
	long status;
	struct pstruct *poptr;

	for (poptr = (struct pstruct *)PList.mlh_Head;
		poptr->node.mln_Succ;
		poptr = (struct pstruct *)poptr->node.mln_Succ)
		{
		if (poptr->fptr1 == fptr)
			{
			Remove(poptr);
			fclose(poptr->fptr1);
			if (!WaitChild(poptr->child, &status))
				{
				status = -1;
				}
			free(poptr);
			return(status);
			}
		}

	return -1;
	}

static unsigned short Generation = 0;

static char *hex = "0123456789abcdef";

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

	cp = template;
	cp += strlen(template);

	val = (unsigned long) FindTask(0L);
	while (*--cp == 'X')
		{
		*cp = hex[val%16];
		val /= 16;
		}

	val = Generation++;
	while (*--cp == 'X')
		{
		*cp = hex[val%16];
		val /= 16;
		}

	return template;
	}
