/* popen.c
 *
 * Implementation of popen() / pclose for Atari ST (will eventually work for
 * MSDOS too). The pipe is simulated by a temporary file. If caller wants to
 * read the pipe, then execute the command in popen and return a descriptor
 * to the output delivered by the command. If caller wants to write to the
 * pipe, then preserve the callers output in a temp file and execute the
 * command on pclose.
 * Since it is necessary to keep some information from popen() up to the
 * pclose(), we keep all opened pipes in a list and fill up the entries as
 * needed. Pipes are identified by their FILE descriptor returned from the
 * fopen() call.
 *
 * Revision 1.2, jrb 08-15-89	applied kai's diffs
 *	gcc-lib's system() now does re-direction. Changes to
 *	for re-direction stuff because of this.
 *
 * Revision 1.1, kub 05-23-89
 * written 89/05/23 by Kai-Uwe Bloem (I5110401@DBSTU1.BITNET for now)
 */

#include	<stdio.h>
#include	<memory.h>
#include	<fcntl.h>
#include	<string.h>
#include	<unistd.h>

struct _pipe {
	char	pmode;		/* "r" or "w" to/from the pipe ?	*/
	char	*pcommand;	/* cmd to execute (only for "w")	*/
	int	pstat;		/* exit code of cmd (only for "r")	*/
	char	pname[32];	/* temp filename. 32 should be enough...*/
	FILE	*pfile;		/* pipe identifier			*/
	struct _pipe	*pnext;	/* next pipe in the list.		*/
	};

static struct _pipe	*__pipes = NULL;	/* head of pipe list	*/

/* open a pipe to the command argument in the mode given by the type string.
 * If caller wants to read the pipe, redirect output and execute command,
 * then open the temp file and return that. If caller wants to write to
 * the pipe, create the temp file and return that.
 */
FILE *popen(command, type)
const char	*command, *type;
{
	struct _pipe *p;	/* the new pipe's list entry	*/

	/* get space for the new pipe. If we can't get it then that's that */
	p = (struct _pipe *) malloc(sizeof(struct _pipe));
	if (p == NULL) return (FILE *) NULL;

	/* initialize the new pipe entry */
	p->pmode = *type;
	p->pfile = (FILE *) NULL;
	(void)tmpnam(p->pname);
	/* make command line with appropriate re-direction */
	if((p->pcommand = (char *)malloc(strlen(command)+strlen(p->pname)+4L))
		== (char *)NULL)
	{	/* no  mem */
		free(p);
		return (FILE *)NULL;
	}
	strcpy(p->pcommand, command);
	strcat(p->pcommand, (p->pmode == 'r')? " >" : " <");
	strcat(p->pcommand, p->pname);

	/* Everything has now been set up to really execute the pipe request */
	switch(p->pmode)
	{
	case 'r':	/* reading */
		/* execute the command, output redir. to tmp pipe file */
		p->pstat = system(p->pcommand);
		if (p->pstat < 0) {
			unlink(p->pname);
			return (FILE *)NULL;
		}
		p->pfile = fopen(p->pname,"r");	/* now open the pipe	*/
		break;
	case 'w':	/* writing */
		/* open tmp pipe file */
		p->pfile = fopen(p->pname,"w"); /* open the pipe file	*/
		break;
	}

	if (p->pfile)
	{	/* pipe successfully established. Link it to the list	*/
		p->pnext = __pipes;
		__pipes = p;
		return p->pfile;
	}
	else
	{	/* if there is no pipe identifier then throw it away	*/
		unlink(p->pname);
		free(p->pcommand);
		free(p);
		return (FILE *) NULL;
	}
}

/* close a pipe created by popen(). If caller wanted to read the pipe, then
 * just do the cleanup and return the command's status code. If caller wanted
 * to write to the pipe, the work has just started. Close the tempfile, call
 * the command with input redirected to the temp file and cleanup after that.
 */
int pclose(fp)
FILE	*fp;
{
	struct _pipe	*p,		/* the pipe's list element	*/
			*q;		/* predecessor of p in the list	*/
	int	status = -1;		/* return status of the command	*/

	/* search the pipe list for a pipe matching the FILE descriptor	*/
	for (p = __pipes, q = NULL;  p && p->pfile != fp;  q = p, p = p->pnext);
	if (p == NULL)		/* Never had a popen() for this file...	*/
		return status;	/* this pclose call makes no sense !	*/

	fclose(p->pfile);		/* close temp file		*/
	switch(p->pmode)
	{
	case 'w':	/* writing */
		status = system(p->pcommand);	/* execute the command	*/
		break;
	case 'r':	/* reading */
		status = p->pstat;	/* just get status code		*/
		break;
	}
	unlink(p->pname);		/* temp file no longer needed	*/

	/* remove the pipe from the list */
	if (q)	/* not the first in the list, unlink it from previous pipe */
		q->pnext = p->pnext;
	else	/* first in the list, set new list head	*/
		__pipes = __pipes->pnext;

	/* Now free the pipe entry */
	free(p->pcommand);
	free(p);

	return status;
}
