/******************************************************************************
 * NOTE: This file has been modified for use with MSDOS and the WATCOM C/386
 * compiler.  Darryl Okahata, April 1994.
 *****************************************************************************/

/* $RCSfile: popen.c,v $$Revision: 4.0.1.1 $$Date: 91/06/07 11:22:52 $
 *
 *    (C) Copyright 1988, 1990 Diomidis Spinellis.
 *
 *    You may distribute under the terms of either the GNU General Public
 *    License or the Artistic License, as specified in the README file.
 *
 * $Log:	popen.c,v $
 * Revision 4.0.1.1  91/06/07  11:22:52  lwall
 * patch4: new copyright notice
 * 
 * Revision 4.0  91/03/20  01:34:50  lwall
 * 4.0 baseline.
 * 
 * Revision 3.0.1.2  90/08/09  04:04:42  lwall
 * patch19: various MSDOS and OS/2 patches folded in
 * 
 * Revision 3.0.1.1  90/03/27  16:11:57  lwall
 * patch16: MSDOS support
 * 
 * Revision 1.1  90/03/18  20:32:20  dds
 * Initial revision
 *
 */

/*
 * Popen and pclose for MS-DOS
 */

#include <stdlib.h>
#include <stdio.h>
#include <process.h>
extern char **origenviron;
#ifdef __WATCOMC__
extern char **__near environ;
extern char *tempnam();
#else
extern char **environ;
#endif


#define FAKE_PWD	/* #define to intercept popen()'s to "pwd" and fake it
			   internally */

#ifdef FAKE_PWD
# include <direct.h>
# include <sys/types.h>
# include <fcntl.h>
# include <io.h>
#endif

/*
 * Possible actions on an popened file
 */
enum action {
	delete, 			/* Used for "r". Delete the tmp file */
	execute				/* Used for "w". Execute the command. */
};

/*
 * Linked list of things to do at the end of the program execution.
 */
static struct todo {
	FILE *f;			/* File we are working on (to fclose) */
	const char *name;		/* Name of the file (to unlink) */
	const char *command;		/* Command to execute */
	enum action what;		/* What to do (execute or delete) */
	struct todo *next;		/* Next structure */
} *todolist;


/* Clean up function */
static int close_pipes(void);

/*
 * Add a file f running the command command on file name to the list
 * of actions to be done at the end.  The action is specified in what.
 * Return -1 on failure, 0 if ok.
 */
static int
add(FILE *f, const char *command, const char *name, enum action what)
{
	struct todo    *p;

	if (((p = (struct todo *) malloc(sizeof(struct todo))) == NULL) ||
	    ((p->command = (char *) malloc(strlen(command) + 1)) == NULL))
		return -1;
	p->f = f;
	strcpy(p->command, command);
	p->name = name;
	p->what = what;
	p->next = todolist;
	todolist = p;
	return 0;
}


static int xsystem(cmd)
char	*cmd;
{
    char		*fixed_cmd;
    int			result;
    extern char		*msdos32_fix_cmd();

    fixed_cmd = msdos32_fix_cmd(cmd);
    result = system(fixed_cmd);
    free(fixed_cmd);
    return (result);
}


FILE *
my_popen(const char *command, const char *t)
{
	char buff[1000];
	char *name;
	FILE *f;
	static init = 0;

	if (!init)
		if (onexit(close_pipes) == NULL)
			return NULL;
		else
			init++;

	if ((name = tempnam((char*)NULL, "pp")) == NULL)
		return NULL;

	switch (*t) {
	case 'r':
#ifdef FAKE_PWD
		if (strcmp(command, "pwd") == 0) {
			int	fd, len;
			char	*wd;

			if (((fd = open(name, O_WRONLY|O_CREAT|O_TRUNC|O_TEXT,
					0666)) < 0) ||
			    ((wd = getcwd(NULL, 0)) == NULL)
			    ) {
				free(name);
				return (NULL);
			}
			len = strlen(wd);
			if ((write(fd, wd, len) != len) ||
			    (write(fd, "\n", 1) != 1) ||
			    (close(fd) != 0)
			    ) {
				free(name);
				free(wd);
				return (NULL);
			}
			free(wd);
			if ((f = fopen(name, "r")) == NULL) {
				free(name);
				return (NULL);
			}
		} else {
#endif
			sprintf(buff, "%s >%s", command, name);
			if (xsystem(buff) == -1 ||
			    (f = fopen(name, "r")) == NULL) {
				free(name);
				return NULL;
			}
#ifdef FAKE_PWD
		}
#endif
		if (add(f, command, name, delete)) {
			(void)fclose(f);
			(void)unlink(name);
			free(name);
			return NULL;
		}
		return f;
	case 'w':
		if ((f = fopen(name, "w")) == NULL) {
			free(name);
			return NULL;
		}
		if (add(f, command, name, execute)) {
			(void)fclose(f);
			(void)unlink(name);
			free(name);
			return NULL;
		}
		return f;
	default:
		free(name);
		return NULL;
	}
}

int
my_pclose(FILE *f)
{
	struct todo *p, **prev;
	char buff[256];
	const char *name;
	int status;

	for (p = todolist, prev = &todolist; p; prev = &(p->next), p = p->next)
		if (p->f == f) {
			*prev = p->next;
			name = p->name;
			switch (p->what) {
			case delete:
				free(p->command);
				free(p);
				if (fclose(f) == EOF) {
					(void)unlink(name);
					status = EOF;
				} else if (unlink(name) < 0)
					status = EOF;
				else
					status = 0;
				free((void*)name);
				return status;
			case execute:
				(void)sprintf(buff, "%s <%s", p->command, p->name);
				free(p->command);
				free(p);
				if (fclose(f) == EOF) {
					(void)unlink(name);
					status = EOF;
				} else if (xsystem(buff)) {
					(void)unlink(name);
					status = EOF;
				} else if (unlink(name) < 0)
					status = EOF;
				else
					status = 0;
				free((void*)name);
				return status;
			default:
				return EOF;
			}
		}
	return EOF;
}

/*
 * Clean up at the end.  Called by the onexit handler.
 */
static int
close_pipes(void)
{
	struct todo    *p;

	for (p = todolist; p; p = p->next)
		(void)my_pclose(p->f);
	return 0;
}
