#include <string.h>
#include <stdio.h>
#include <functions.h>

/* mypopen is a Unix-like "popen" for the Amiga.

   This can be done cleaner using ARP's "ASynchRun", but for this revision, I
   wanted to make it work without ARP.  For now we use the Amiga command:

		Execute ("run <NULL: >NULL: command >out <in", nil, nil);

   Logic:
	A. Make a pipe:temp file.
	B. Parse the command handed to popen.  The string to the left of the
	   first space is the "command".

	   If the next space-delimited string starts with '>' or '<', save it
	   as "out" or "in", then check the second string for '>' or '<'.
	  
	C. If we are being opened for "write" (mode argument of "w"):
		
		- If a '>' wasn't specified, use NULL:

		- The '<' should be attached to the pipe whose other end we
		  return.  If a '<' *was* specified in the cmd argument, then
		  the user probably screwed up, since popen(cmd,"w") is called
		  only in response to the 'open (handle,"|command")' statement.

		  To keep this kind of mistake from producing system lockups,
		  we open NULL: and return it.  The command is executed and the
		  user can write to NULL: all day long.

	   If we are being opened for "read" (mode argument of "r"):
		
		- If a '<' wasn't specified, use NULL:

		- The '>' should be attached to the pipe whose other end we
		  return.  If a '>' *was* specified in the command line, then
		  the user probably wants only the command's side-effects.
		  (`command >outfile`; is legal.)

		  To keep lockouts from happening, we open NULL: and return it.
		  The command is executed, returning immediately, and the
		  user will get an EOF on the first read.

	   NOTE: The logic is a little strange because the Amiga Pipe device
		 must have the read side open when the write side does a quick
		 open/close without sending any data, or the read side hangs.

   C. Execute the command, which should return immediately.
   D. If "w" mode "fopen()" our end of pipe:temp and return it.

   NOTE: If the cmd argument is a single "-", we are supposed to "fork" and
	 attach ourselves to the STDOUT/STDIN of the child.  Since we can't
	 fork (or at least I don't know how), some of the functionality is
	 duplicated by "run"ning another "perl" and  attaching to it.  This
	 does *not* allow the 'open(a,"|-") || exec ("produce ouput")' hack.
*/
#define PIPEPATH "pipe:popenXXXXXXXX"
#define AMIGANULLFILE	"NULL:"
#define RUNBACK		"run >NULL: <NULL: "

FILE *mypopen(char *cmd, char *mode)
{
    int exrtn;
    struct FileHandle *nil;
    char tmpcmd[255], realcmd[255], pipefile[32];
    char *in, *out, *args;
    FILE *fp = NULL;

    while (*cmd == ' ') cmd++;
    if (*cmd == '-' && !*(cmd+1)) cmd = "perl";

    if (strlen(cmd) + sizeof(RUNBACK) + 1 > sizeof(realcmd)) return((FILE *)0);

    strcpy (tmpcmd, cmd);
    amigaizepath(tmpcmd);
    in = out = NULL;
    if (args = strchr(tmpcmd, ' ')) {	/* found something beyond command */
	*args++ = '\0';
inverted:
	if (!in && *args == '<') {
	    in = ++args;
	    if (args = strchr(args, ' '))
		*args++ = '\0';
	}
	if (!out && *args == '>') {
	    out = ++args;
	    if (args = strchr(args, ' '))
		*args++ = '\0';
	    goto inverted;
	}
    }
    if (!cmdexists(tmpcmd)) return((FILE *)0);
    strcpy(pipefile, PIPEPATH);
    mktemp(pipefile);

    if (*mode == 'w') {
	if (in)
	    fp = fopen(AMIGANULLFILE, mode);
	else
	    in = pipefile;

    	if (!out) out = AMIGANULLFILE;
    } else {
	if (out)
	    fp = fopen(AMIGANULLFILE, mode);
	else {
	    out = pipefile;
	    fp = fopen(pipefile, mode);
	}

    	if (!in) in = AMIGANULLFILE;
    }
    strcpy (realcmd, RUNBACK);
    strcat (realcmd, tmpcmd);
    if (out) {
	strcat (realcmd, " >");
	strcat (realcmd, out);
    }
    if (in) {
	strcat (realcmd, " <");
	strcat (realcmd, in);
    }
    if (args) {
	strcat (realcmd, " ");
	strcat (realcmd, args);
    }

    nil = (struct FileHandle *)Open(AMIGANULLFILE, MODE_OLDFILE);
    exrtn = Execute(realcmd, nil, nil);
    Delay(20);	/* give the new process a small chance to get started */

    Close((BPTR)nil);

    if (!exrtn) {
	return ((FILE *)0);
    }
    if (!fp)
	return(fopen(pipefile, mode));
    else
	return(fp);
}

/* This should do nothing other than fclose the STDIO structure.  Unlike
   Unix, we have no connection with the created process.  We certainly
   don't have to "wait()" for it.  Someday, maybe AmigaDOS will acquire
   a process tree structure like Unix, but I'm not holding my breath.
*/
int mypclose(FILE *ptr)
{
    if (!ptr) return (-1);
    fclose(ptr);
    return(0);
}
