#include <stdio.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "popen.h"
#include <errno.h>
#include <malloc.h>
#include <signal.h>

#define D	if(0)

LOCAL void boom() {}

#define BOOM	boom()

struct popen_s
thr_popen(char **argv, C char *type, int errfd, int iofd)
{
 struct popen_s rv;
 rv.fp = 0;
 rv.pid = -1;

 unsigned fs = 1;
 int fd[2];
 BOOM;
 if(access(argv[0], X_OK) == -1)
	return rv;

 if(pipe(fd) < 0)
	return rv;

 int pid;
 do	{
	BOOM;
	pid = fork1();
	if(pid < 0)	{
		fprintf(stderr, "#WARN: fork fails in thr_popen - retrying\n");
		sleep(fs);
		fs *= 2;
		}
	} while(pid<0 && fs<300);

 if(pid < 0)	{	/* fork failure */
	close(fd[0]);
	close(fd[1]);
	return rv;
	}
 else if(pid == 0)	{	/* child */
	if(type[0] == 'r')	{
		close(fd[0]);
		dup2(fd[1], fileno(stdout));
		close(fd[1]);
		if(iofd >= 0)
			dup2(iofd, fileno(stdin));
		}
	else	{
		close(fd[1]);
		dup2(fd[0], fileno(stdin));
		close(fd[0]);
		if(iofd >= 0)
			dup2(iofd, fileno(stdout));
		}
	if(errfd >= 0)
		dup2(errfd, fileno(stderr));
	D {
		char **p;
		fprintf(stderr, "EXECCING(pid=%d, PID=%d):", pid, getpid());
		for(p=argv; *p;p++)
			fprintf(stderr, " %s", *p);
		fprintf(stderr, "\n");
		}
	execv(argv[0], argv);

	perror("INFO: thr_popen: child:");
	fprintf(stderr, "ERR: thr_popen: child: exec of %s failed\n", argv[0]);

	_exit(127);
	}
 else	{		/* parent */
	BOOM;
	if(type[0] == 'r')	{
		close(fd[1]);
		rv.fp = fdopen(fd[0], type);
		assert(rv.fp);
		}
	else	{
		close(fd[0]);
		rv.fp = fdopen(fd[1], type);
		assert(rv.fp);
		}
	rv.pid = pid;
	}
 return rv;
}


int thr_pclose(popen_s& s)
{
 int err, stat;
 BOOM;
 assert(s.pid != 0);
 if(fclose(s.fp) != 0)	{
	fprintf(stderr, "#WARN: Bad close of s.fp in pclose\n");
	kill(s.pid, SIGTERM);
	}
 while((err=waitpid(s.pid, &stat, 0))==(-1) && errno==EINTR)
	;
 if(err == (-1))	{
	perror("ERR: opt.c: error on waitpid");
	}

 return err==(-1) ? -1 : stat;
}
