/*
 * @DEC_COPYRIGHT@
 */
/*
 * HISTORY
 * Revision 1.2  1995/03/09  21:51:03  bourquard
 * port to NT
 *
 * Revision 1.1.4.2  1994/04/08  19:05:41  Susan_Ng
 * 	code drop for post-Sterling fixes/I18N changes
 * 	[1994/04/07  18:19:31  Susan_Ng]
 *
 * Revision 1.1.2.2  1992/08/03  09:48:43  Dave_Hill
 * 	initial port to alpha
 * 	[92/08/03  09:38:28  Dave_Hill]
 * 
 * Revision 1.1  90/01/01  00:00:00  devrcs
 * 	Initial load into Alpha pool
 * 
 * Revision 1.2  91/12/30  12:48:20  devbld
 * 	Initial load of project
 * 
 */
/*
 * Copyright 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
 * 
 *                         All Rights Reserved
 * 
 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 */

/*
 *
 *	dxdiff
 *
 *	invokediff.c - invokes diff pipeline to process info
 *
 *	Author:	Laurence P. G. Cable
 *
 *	Created : 22nd April 1988
 *
 *
 *	Description
 *	-----------
 *
 *
 *	Modification History
 *	------------ -------
 *	
 *	April 25th 1988		Laurence P. G. Cable
 *
 *	Changed return status code to ensure that error message is only
 *	displayed when diff returns 2 to indicate an error.
 */

static char sccsid[] = "@(#)invokediff.c	1.10	17:45:25 2/21/89";


#include <stdio.h>
#include <X11/Xlib.h>
#ifdef WIN32
#include <X11/xfuncs.h>
#endif WIN32			   	
#include <Xm/Xm.h>

#ifdef WIN32
#define off_t _off_t
#include <limits.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <process.h>
#else
#include <sys/limits.h>
#include <sys/wait.h>
#endif WIN32

#include <fcntl.h>

#include "y.tab.h"
#include "filestuff.h"
#include "parsediff.h"
#include "alloc.h"
#include "dxdiff.h"

#define	WRITEFD 1
#define	READFD  0

#ifndef WIN32
extern	char **environ;
#else
char **environ;
HANDLE hPipeRdDup;
#endif WIN32

/********************** Private Routines ************************/


/********************************
 *
 *     FreeEnvironmentList
 *
 ********************************/

static 
FreeEnvironmentList(env)
	char **env;
{
	register char **p;

	if (env == (char **)NULL)
		return;

	for (p = env; *p; p++)
		XtFree(*p);

	XtFree((char *)env);
}

/********************************
 *
 *     CreateEnvironmentList
 *
 ********************************/

static char **
CreateEnvironmentList(shellvars)
	EnvVariables	**shellvars;
{
	int		nenv = 0,
			noff = 0;
	char		**myenv = (char **)NULL;
	register char	**p,**q;
	EnvVariables	**sp;
	

	if (environ != (char **)NULL) {
		for (p = environ; *p; p++, nenv++)
		;
		noff = nenv;
	}

	if (shellvars != (EnvVariables **)NULL) 
		for (sp = shellvars; *sp; sp++, nenv++)
		;

	if ((myenv = (char **)XtMalloc(nenv * sizeof (char *) + 1)) == 
	    (char **)NULL) {	/* error */
		return myenv;
	}
	myenv[nenv] = (char *)NULL;

	for (p = environ, q = myenv; q < myenv + noff; p++, q++) {
		int l;

		if ((*q = (char *)XtMalloc((l = strlen(*p) + 1))) 
		    == (char *)NULL) {	/* error */
			FreeEnvironmentList(myenv);
			return (char **)NULL;
		}
		bcopy(*p, *q, l);
	}

	for (sp = shellvars; q < myenv + nenv; sp++, q++) {
		int	sl,vl;
		Boolean notnull; 

		sl = strlen((*sp)->shellvariable); 
		vl = ((notnull = ((*sp)->value != (char *)NULL)) ? strlen((*sp)->value) : 0) + 1;

		if ((*q = (char *)XtMalloc(sl + vl)) == (char *)NULL) {
			FreeEnvironmentList(myenv);
			return (char **)NULL;
		}

		bcopy((*sp)->shellvariable + 1, *q, sl--);
		(*q)[sl] = '=';
		if (notnull) {
			bcopy((*sp)->value, (*q) + ++sl, vl);
		} else {
			(*q)[sl + 1] = '\0';
		}
	}

	return myenv;
}

/********************************
 *
 *     _InvokeDiff
 *
 ********************************/


static Boolean
#ifdef WIN32
_InvokeDiff(name,dlb)
#else
_InvokeDiff(name,argv,environ,dlb)
#endif WIN32
	char 			 *name;
#ifndef WIN32
	char			 **argv,
				 **environ;
#endif WIN32
	register DiffListBlkPtr  dlb;
{
#ifndef WIN32		/* define these routines differently on NT */
	int		pid;
	int		pipefds[2];
	int		ret;
#ifdef __osf__
	unsigned long	status;
#else
	union	wait	status;
#endif

	if (pipe(pipefds)               == -1) {	/* error */
		perror("failed pipe(2) in _InvokeDiff\n");
		return False;
	}

	if ((pid = vfork())) {	/* parent */
		close(pipefds[WRITEFD]);

		if (pid == -1) {	/* error */
			perror("failed vfork(2) in _InvokeDiff\n");
			close(pipefds[READFD]);
			return False;
		}

		SetyylexInputStream(pipefds[READFD]);

		parsediff(dlb);

		while ((ret = wait(&status)) != -1 && ret != pid)
		;

		if (ret == -1) {	/* error */
			perror("bad wait(2) in _InvokeDiff\n");
		}
		close(pipefds[READFD]);

#ifdef __osf__
		return ( ret == pid && WEXITSTATUS(status) < 2);
#else
		return ( ret == pid && status.w_retcode < 2);
#endif __osf__
	} else {		/* child */
		close(pipefds[READFD]);
		close(1);	/* stdout */
		close(2);	/* stderr */

		if ((ret = fcntl(pipefds[WRITEFD], F_DUPFD, 1)) == -1 ||
		     ret != 1) { /* error */
			_exit(1);
		}	/* get the pipe on stdout */

		if ((ret = fcntl(pipefds[WRITEFD], F_DUPFD, 2)) == -1 ||
		     ret != 2) { /* error */
			_exit(1);
		}	/* get the pipe on stderr */

		close(pipefds[WRITEFD]); /* throw it away */

		execve(name, argv, environ);
		perror("failed execve(2) in _InvokeDiff\n");
		close(ret);
		_exit(1);	/* now we are in trouble! */
	}

#else	  
    /* definition of _InvokeDiff for Windows NT */
	HANDLE				hSaveStdOut,hTmpFile;
	SECURITY_ATTRIBUTES saAttr;
	STARTUPINFO			siStartInfo;
	PROCESS_INFORMATION piProcInfo;
	LPDWORD				childStatus;

	/* bug workaround?? */
    AllocConsole();

	/* Set the inherit flag so handles are inherited */
	saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
	saAttr.bInheritHandle = TRUE;
	saAttr.lpSecurityDescriptor = NULL;

    /* Here's the psuedo-code:
		  - save parent's stdout
		  - Create the temporary file
	      - set stdout to a temporary file
		  - create child (which invokes diff and inherits stdout from parent)
		  - restore parent's stdout
		  - save parent's stdin
		  - set parent's stdin to file
		  - parse child's output
		  - wait for child to complete
		  - restore parent's stdin
	 */

	 /* invoke diff in the child process */
	 siStartInfo.cb = sizeof(STARTUPINFO);
	 siStartInfo.lpReserved = NULL;
	 siStartInfo.lpReserved2 = NULL;
	 siStartInfo.cbReserved2 = 0;
	 siStartInfo.lpDesktop = NULL;
	 siStartInfo.lpTitle = NULL;
	 siStartInfo.dwFlags = 0;

	 /* Save the parents stdout */
	 hSaveStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

	 hTmpFile = CreateFile("foo.tmp",
	 					   GENERIC_WRITE, 
	 					   FILE_SHARE_READ, /* allow readers */
						   &saAttr,			/* security attributes */
						   CREATE_ALWAYS,	/* overwrite if it already exists */
						   FILE_ATTRIBUTE_NORMAL,	/* try normal since I saw a 0-byte file
						   							   which had ~60 bytes of info when I typed it */
						   0);				/* no template file */

	/* Set parent's stdout to the file (so child will write to it) */
	if (!SetStdHandle(STD_OUTPUT_HANDLE, hTmpFile))
	    err_sys("error setting STDOUT\n");

	if (!CreateProcess(  NULL,				/* image name, but use command line instead to insure
	 										 * that NT follows the PATH for the DIFF image
	 										 */
	 					 name,				/* command line */
						 NULL,				/* process security attributes */
						 NULL,				/* primary thread security attributes */
						 TRUE,				/* handles are inherited */
						 NORMAL_PRIORITY_CLASS,					
						 					/* creation flags */
						 NULL,				/* use parent's environment */
						 NULL,				/* use parent's current directory */
						 &siStartInfo,		/* STARTUPINFO pointer */
						 &piProcInfo))		/* receives PROCESS_INFORMATION */
		 {
		 err_sys("CreateProcess failed in _InvokeDiff\n");
		 return False;
		 }

     /* restore parent's stdout */
	 SetStdHandle(STD_OUTPUT_HANDLE, hSaveStdOut);

	 /* setup input stream for lex */
	 SetyylexInputStream(0 /* unused */);

	 /* wait for child to complete 
	  * *** this used to be done after parsing the output from the "child"
	  * but I'm hypothesizing that I was running into a race condition. Let's
	  * make sure that the child has written to the file before trying to parse
	  * any of the child's output 
	  */ 
	 WaitForSingleObject(piProcInfo.hProcess, INFINITE);

	 /* process the input received from child */
     parsediff(dlb);

     /* Delete the temporary file */
	 if (!CloseHandle(hTmpFile))
	     err_sys("failed to close handle to 'foo.tmp'");	
	 CloseyylexInputStream();
	 if (!DeleteFile("foo.tmp"))
	     err_sys("failed to delete 'foo.tmp'");
     FreeConsole();

     /* return with status of child */
	 if (!GetExitCodeProcess(piProcInfo.hProcess, &childStatus))
	    {
	    err_sys("failed to get exit status of child in _InvokeDiff");
		return FALSE;
		}
	 else
	 	return (childStatus < 2);
} 
#endif WIN32

/********************** Public Routines ************************/

/********************************
 *
 *     InvokeDiff
 *
 ********************************/

static EnvVariables	lf = { DXDIFFSHELLVARL, (char *)NULL },
			rf = { DXDIFFSHELLVARR, (char *)NULL },
			*shv[] = { &lf, &rf, (EnvVariables *)NULL };

Boolean
InvokeDiff(argv, leftfile, rightfile, dlb)
	char			 **argv,
				 *leftfile,
				 *rightfile;
	register DiffListBlkPtr  dlb;
{
	Boolean		ret;
	char		**myenv;
	char		*diffcmd;
	int			cmdlen;

#ifndef WIN32
	lf.value = leftfile;
	rf.value = rightfile;
  
	myenv = CreateEnvironmentList(shv);

	ret  = _InvokeDiff(argv[0], ((argv[1]) ? argv + 1 : (char **)NULL),
	           myenv, dlb);

	FreeEnvironmentList(myenv);
#else
    /* Create the diff command line by appending the filenames to
	 * the command string 
	 */
    cmdlen = strlen(argv[0]) + 
				2   +	/* 1 space and trailing NULL */
				strlen(leftfile) + strlen(rightfile);
	diffcmd = XtMalloc(cmdlen);   /* allocate the space */
	strcpy(diffcmd, argv[0]);	 /* initialize with diff command */
	strcat(diffcmd, leftfile);	 /* append left filename */
	strcat(diffcmd, " ");		 /* add a space separator */
	strcat(diffcmd, rightfile);	 /* append the right filename */

	ret  = _InvokeDiff(diffcmd, dlb);

	/* free the space allocated for the command line */
	XtFree(diffcmd);
#endif WIN32	

	return ret;
}

