/*
 * @DEC_COPYRIGHT@
 */
/*
 * HISTORY
 * Revision 1.2  1995/04/20  10:03:22  zima
 *      Build fixups, re-enable argc/argv usage.
 *
 * Revision 1.2  1995/03/09  21:49:26  bourquard
 * port to NT
 *
 * Revision 1.1.6.2  1994/04/08  19:03:38  Susan_Ng
 * 	code drop for post-Sterling fixes/I18N changes
 * 	[1994/04/07  18:15:53  Susan_Ng]
 *
 * Revision 2.0.1.6  1993/11/19  18:29:23  uwsmw
 * 	add comments
 * 
 * Revision 1.1.4.2  1993/07/30  19:50:19  Lynda_Rice
 * 	QAR 13541:  Added XtSetLanguageProc() support.
 * 	[1993/07/28  21:31:01  Lynda_Rice]
 * 
 * Revision 1.2  91/12/30  12:48:20  devbld
 * 	Initial load of project
 * 
 * Revision 1.1.2.3  1993/01/05  17:26:03  Don_Haney
 * 	Implement HyperHelp and mnemonics
 * 	[92/12/31  14:52:08  Don_Haney]
 * 
 * Revision 1.1.2.2  92/08/03  09:48:03  Dave_Hill
 * 	initial port to alpha
 * 	[92/08/03  09:37:38  Dave_Hill]
 * 
 * Revision 1.1  90/01/01  00:00:00  devrcs
 * 	Initial load into Alpha pool
 * 
 */
/*
 * 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
 *
 *	dxdiff.c - the main body!
 *
 *	Author:	Laurence P. G. Cable
 *
 *	Created : 22nd April 1988
 *
 *
 *	Description
 *	-----------
 *
 *
 *	Modification History
 *	------------ -------
 *	
 *	25th July 1988	Laurence P. G. Cable
 *
 *	Added John Hoffman's 'merged utility' changes
 *
 *	16 Jan 1990	Colin Prosser
 *
 *	Fixed typo in lineNumberForeground resource name, so it
 *	and -lnfg option now work.  Clears UWS QAR 01560.
 *
 *	02/22/94	Dhiren M. Patel
 *	Fix ootb_bug 163. Now if dxdiff is given a path specification and a
 *	filename (with or without a leading full/relative path) to diff, it 
 *	takes the filename (not including the leading path if there is one) 
 *	and appends it to the directory before performing a diff.
 *	The diff is performed on the files resulting from the above operation.
 *	
 *	Fix ootb_bug 177. If dxdiff is given binary file(s)  to diff, a dialog 
 *	box is put up informing the user of this and the files are NOT loaded.
 *	This is applicable whether the files are specified on the command line
 *	or selected from file selection boxes. Individual messages are put
 *	for each attempt to open a binary file.
 */

static char sccsid[] = "@(#)dxdiff.c	1.10	19:00:26 1/16/90";
 
#include <fcntl.h>
#include <stdio.h>
#include <X11/Xlib.h>
#include <Xm/Xm.h>
#include <Xm/MessageB.h>
#include <Mrm/MrmPublic.h>
#include <X11/StringDefs.h>
	
#include <sys/types.h>

#ifdef WIN32
#include <stdlib.h>
#define argc __argc
#define argv __argv
#define stat _stat 
#endif WIN32

#include <sys/stat.h> 
#include "dxdiff.h"
#include "arglists.h"
#include "y.tab.h"
#include "filestuff.h"
#include "parsediff.h"
#include "alloc.h"
#include "differencebox.h"
#include "menu.h"
#include "text.h"
#include "display.h"
#include "displaymenu.h"
#ifndef WIN32
#include "globals.h"			/* to define dxdiff_hierarchy */
#else
MrmHierarchy	dxdiff_hierarchy;
//#include "smrtheap.h"
#define WIN32_LEAN_AND_MEAN 1
#include "windows.h"
#define MSGBOX_TITLE "Dxdiff: Message"
#endif WIN32
#include "dxdiff_proto.h"		/* Function proto's */

/* dp:	status returned by CheckFileNames() 
 *	and used in main().
 */
#define	SUCCESS			0
#define	BAD_STAT_L_FILE		-1
#define	BAD_STAT_R_FILE		-2
#define	BAD_STAT_BOTH		-3
#define	BOTH_DIRS		-4

extern DxDiffDisplayPtr CreateDxDiffDisplay ();

extern int CheckForBinaryFile PROTO ((int f));
extern void Msg_messageparams ();
extern Cardinal Msg_putmessage PROTO ((	Widget parent,
					char *uil_index,
					Display *disp,
					MrmHierarchy diff_hierarchy));

int CheckFileNames PROTO (( char **l_file, char **r_file ));


/* application options */

struct _application_options app_options;



int	defaultwidth,defaultheight;



XtResource resources[] = {
	{
		"slaveVerticalScrolling",
		"",
		XtRBoolean,
		sizeof (Boolean),
		XtOffset(application_options_ptr,slaveverticalscrolling),
		XtRString,
		"on"
	},
	{
		"slaveHorizontalScrolling",
		"",
		XtRBoolean,
		sizeof (Boolean),
		XtOffset(application_options_ptr,slavehorizontalscrolling),
		XtRString,
		"on"
	},
	{
		"displayLineNumbers",
		"",
		XtRBoolean,
		sizeof (Boolean),
		XtOffset(application_options_ptr,displaylinenumbers),
		XtRString,
		"off"
	},
	{
		"drawDiffsAsLines",
		"",
		XtRBoolean,
		sizeof (Boolean),
		XtOffset(application_options_ptr,drawdiffsaslines),
		XtRString,
		"on"
	},
	{
		"debug",
		"",
		XtRBoolean,
		sizeof (Boolean),
		XtOffset(application_options_ptr,Debug),
		XtRString,
		"off"
	},
	{
		"lineNumberForeground",
		"",
		XtRPixel,
		sizeof (app_options.linenumberforeground.pixel),
		XtOffset(application_options_ptr,linenumberforeground.pixel),
		XtRString,
		"black"
	}
};
int	   noofresources = sizeof (resources) / sizeof (resources[0]);

static XrmOptionDescRec options[] = {
	{
		"-sv",
		"slaveVerticalScrolling",
		XrmoptionNoArg,
		"off"
	},
	{
		"+sv",
		"slaveVerticalScrolling",
		XrmoptionNoArg,
		"on"
	},
	{
		"-sh",
		"slaveHorizontalScrolling",
		XrmoptionNoArg,
		"off"
	},
	{
		"+sh",
		"slaveHorizontalScrolling",
		XrmoptionNoArg,
		"on"
	},
	{	"-ln",
		"displayLineNumbers",
		XrmoptionNoArg,
		"off"
	},
	{
		"+ln",
		"displayLineNumbers",
		XrmoptionNoArg,
		"on"
	},
	{	"-dl",
		"drawDiffsAsLines",
		XrmoptionNoArg,
		"off"
	},
	{
		"+dl",
		"drawDiffsAsLines",
		XrmoptionNoArg,
		"on"
	},
	{
		"-debug",
		"debug",
		XrmoptionNoArg,
		"off"
	},
	{
		"+debug",
		"debug",
		XrmoptionNoArg,
		"on"
	},
	{
		"-lnfg",
		"lineNumberForeground",
		XrmoptionSepArg,
		""
	}
};


char			**cmdlinefilelist;
int			cmdlinenoffiles;
Widget			toplevel;	/* now global */
XtAppContext		app_context ;
CoreArgList		initcore;
DxDiffDisplayPtr	maindxdiffdisplay;

#ifdef HYPERHELP

Opaque	help_context;

/* Error processing function for HyperHelp system */

void help_error(problem_string, status)
    char    *problem_string;
    int     status;

{
    fprintf(stderr, "%s, %x\n", problem_string, status);
    exit(0);
}

#endif

#ifdef I18N_MULTIBYTE
MrmHierarchy dxdiff_hierarchy;	/* MRM database hierarchy id */
#endif  /* I18N_MULTIBYTE */
 
#ifdef	COMBINE
diff_main(argc, argv, e)
	int	argc;
	char	**argv,
		**e;
#else
#ifdef WIN32
int WINAPI WinMain(hThisInstance, hPrevInstance, cmdLine, showState)
    HINSTANCE 	hThisInstance,
				hPrevInstance;
	LPSTR		cmdLine;
	int			showState;
#else
main(argc, argv, e)
	int	argc;
	char	**argv,
		**e;
#endif WIN32
#endif COMBINE
{
	short	w,h;
	Arg	wandh[2];

	int f1, f2;
	int f1_ascii, f2_ascii;
	int both_dirs = 0, bad_stat = 0;
	int f1_open_fail = 0, f2_open_fail = 0;
#ifdef WIN32
	char *msgstr;
#endif /* WIN32 */
 

#ifdef MEM_DEBUG
//    dbgMemSetSafetyLevel(MEM_SAFETY_DEBUG);
#endif


#ifdef HYPERHELP
	MrmInitialize();
	DXmInitialize();
#endif

#ifdef R5_XLIB
        /* Must call XtSetLanguageProc() before XtInitialize(); otherwise,
         * setlocale() would not work.
         */
        XtSetLanguageProc(NULL, NULL, NULL) ;
#endif 

	toplevel = XtInitialize(DXDIFFNAME, DXDIFFCLASS, options, 
				sizeof (options) / sizeof (options[0]),
				&argc, argv);

	app_context = XtWidgetToApplicationContext(toplevel) ;

#ifdef I18N_MULTIBYTE
	/* Open the UID files in the hierarchy */
	if (MrmOpenHierarchy(
		nFiles(db_filename_vec),
		db_filename_vec,
		NULL,
		&dxdiff_hierarchy)
		!=MrmSUCCESS)
	{
#ifdef WIN32
	    MessageBox(	NULL,							/* owner window */
			        "Can't open MRM hierarchy.",	/* message text */
					MSGBOX_TITLE,
					MB_ICONEXCLAMATION || MB_SETFOREGROUND || MB_OK );
#else
		printf("Can't open MRM hierarchy.\n");
#endif WIN32
		exit(0);
	}
#endif /* I18N_MULTIBYTE */

	XtGetApplicationResources(toplevel, (XtPointer)&app_options, 
		resources, noofresources, (ArgList)NULL, 0);

	dxdiffIconCreate(toplevel);

#ifndef WIN32
	fcntl(ConnectionNumber(XtDisplay(toplevel)), F_SETFD, 0x1);
#endif WIN32

	/* create the display */

	InitCoreArgList(initcore);

	defaultwidth = WidthOfScreen(DefaultScreenOfDisplay(XtDisplay(toplevel))) / 2;
	defaultheight = HeightOfScreen(DefaultScreenOfDisplay(XtDisplay(toplevel))) / 2;

	XtSetArg(wandh[0], XmNwidth, &w);
	XtSetArg(wandh[1], XmNheight, &h);
	XtGetValues(toplevel, wandh, 2);

	CoreWidth(initcore) = (w == 0) ? defaultwidth : w;
	CoreHeight(initcore) = (h == 0) ? defaultheight : h;
	CoreDestroyCallBack(initcore) = NULL;

	maindxdiffdisplay = (DxDiffDisplayPtr)CreateNewDxDiffDisplay(
		toplevel, "dxdiffdisplay", &initcore);

	XtRealizeWidget(toplevel);

	if (argc > 2) /* got files on cmd line! */
	{

		/* dp:
		 * if one of the files is a directory, then append the name of
		 * of the other file to it.
		 */
		switch(CheckFileNames(&argv[argc-2], &argv[argc-1]))
		{
		    case BAD_STAT_L_FILE:
			bad_stat = 1;
			break;
		    case BAD_STAT_R_FILE:
			bad_stat = 1;
			break;
		    case BAD_STAT_BOTH:
			bad_stat = 1;
			break;
		    case BOTH_DIRS:
			both_dirs = 1;
			break;
		    default:
			break;
		}


	    if (!bad_stat && !both_dirs)
	    {
	    /* dp:
	     * if we have no bad stats and both files are not directories
	     * continue processing.
	     */
                f1 = open(argv[argc-2], 0);
                if (f1 < 0)
                { 
#ifndef WIN32
		    		Msg_messageparams (1, argv[argc-2]);
		    		sg_putmessage(toplevel,"k_open_failed", 
					tDisplay(toplevel), dxdiff_hierarchy);
#else
					msgstr = XtMalloc( strlen ("open failed ") + strlen( argv[argc-2]) + 1);
					strcat (msgstr, "open failed ");
					strcat (msgstr, argv[argc-2]);
					MessageBox( NULL,				/* owner window handle */
							   msgstr,
							   MSGBOX_TITLE,
					    	   MB_ICONEXCLAMATION || MB_SETFOREGROUND || MB_OK );
					XtFree(msgstr);
#endif WIN32
		    		f1_open_fail = 1;
                }
                f2 = open(argv[argc-1], 0);
                if (f2 < 0)
                {
#ifndef WIN32
		    		Msg_messageparams (1, argv[argc-1]);
		    		Msg_putmessage(toplevel,"k_open_failed", 
					XtDisplay(toplevel), dxdiff_hierarchy);
#else
					msgstr = XtMalloc( strlen ("Unable to open ") + strlen( argv[argc-2]) + 1);
					strcat (msgstr, "Unable to open ");
					strcat (msgstr, argv[argc-2]);
					MessageBox( NULL,				/* owner window handle */
							   msgstr,
							   MSGBOX_TITLE,
					    	   MB_ICONEXCLAMATION || MB_SETFOREGROUND || MB_OK );
					XtFree(msgstr);

#endif WIN32
		    /* dp: 
		     * if we opened file1 and could not open file2 close
		     * file1 as there is no use of leaving it open. We
		     * inform the user of the error and let the user open
		     * files from the file selection box.
		     */
		    if (!f1_open_fail)
			close(f1);
		    f2_open_fail = 1;
                }

		if (!f1_open_fail && !f2_open_fail)
		{
		/* dp:
		 * if we could open both the files, proceed to
		 * check if they are binary or not
 		 */
                    f1_ascii = CheckForBinaryFile(f1);
                    f2_ascii = CheckForBinaryFile(f2);

		    if (!f1_ascii || !f2_ascii)
		    {
		    /* dp:
		     * If at least one of the files is a binary file, 
		     * we need to tell the user about it.
		     */
			if (!f1_ascii && !f2_ascii) /* both files are binary */
			{
#ifndef WIN32
			    Msg_messageparams (2, argv[argc-2], argv[argc-1]);
			    Msg_putmessage(toplevel,"k_both_files_binary", 
					XtDisplay(toplevel), dxdiff_hierarchy);
#else
				msgstr = XtMalloc( strlen ("Files  and  are binary files.")
                                                                + strlen( argv[argc-2]) 
								+ strlen(argv[argc-1]) + 1);
				strcat (msgstr, "Files ");
                                strcat (msgstr, argv[argc-2]);
				strcat (msgstr, " and ");
				strcat (msgstr, argv[argc-1]);
				strcat (msgstr, " are binary files.");
				MessageBox( NULL,				/* owner window handle */
							msgstr,
						    MSGBOX_TITLE,
					    	MB_ICONEXCLAMATION || MB_SETFOREGROUND || MB_OK );
				XtFree(msgstr);
#endif WIN32
			}
			else if (!f1_ascii)         /* first file is binary */
			{
#ifndef WIN32
			    Msg_messageparams (1, argv[argc-2]);
			    Msg_putmessage(toplevel,"k_file_binary", 
					XtDisplay(toplevel), dxdiff_hierarchy);
#else
				msgstr = XtMalloc( strlen ("File  is a binary file.") + strlen( argv[argc-2]) + 1);
				strcat (msgstr, "File ");
                                strcat (msgstr, argv[argc-2]);
				strcat (msgstr, " is a binary file.");
				MessageBox( NULL,				/* owner window handle */
							msgstr,
						    MSGBOX_TITLE,
					    	MB_ICONEXCLAMATION || MB_SETFOREGROUND || MB_OK );
				XtFree(msgstr);
#endif WIN32
			}
			else                        /* second file is binary */
			{ 
#ifndef WIN32
			    Msg_messageparams (1, argv[argc-1]);
			    Msg_putmessage(toplevel,"k_file_binary", 
					XtDisplay(toplevel), dxdiff_hierarchy);
#else
				msgstr = XtMalloc( strlen ("File  is a binary file.") + strlen( argv[argc-1]) + 1);
				strcat (msgstr, "File ");
				strcat (msgstr, argv[argc-1]);
				strcat (msgstr, " is a binary file.");
				MessageBox( NULL,				/* owner window handle */
							msgstr,
						    MSGBOX_TITLE,
					    	MB_ICONEXCLAMATION || MB_SETFOREGROUND || MB_OK );
				XtFree(msgstr);
#endif WIN32
			}
		    }   /* end if (!f1_ascii || !f2_ascii) */
		    else
		    {
		    /* dp:
		     * None of the files are binary files and both of them are 
		     * not directories; load them for diff'ing.
		     */
			cmdlinefilelist = argv;
			cmdlinenoffiles = argc - 1;

			if (DoDiffs(maindxdiffdisplay, 
						argv[argc-2], argv[argc-1]))
			{
			    LoadDiffs(maindxdiffdisplay, 
						argv[argc-2], argv[argc-1],
				DxDiffDisplayPtrDiffList(
						maindxdiffdisplay)->edchead,
				DxDiffDisplayPtrDiffList(
						maindxdiffdisplay)->edctail);
			} 
			else 
			{	/* oops - error */
			    ReportDiffErrors(maindxdiffdisplay);
			}
		    }
		}   /* end if (!f1_open_fail && !f2_open_fail) */
	    }	/* end if (!bad_stat && !both_dirs) */
	}   /* end if (argc > 2) */

#ifdef HYPERHELP
	DXmHelpSystemOpen(&help_context, toplevel, dxdiff_help, help_error,
			  "Help System Error");
#endif

	XtMainLoop();
}



int CheckFileNames
#ifdef DXDIFF_PROTO	/* prototypes */
	(char **l_file, char **r_file)
#else			/* no prototypes */
	(l_file, r_file)
	char **l_file, **r_file;
#endif			/* end ifdef DXDIFF_PROTO */
{ 
	struct stat     l_file_stat, r_file_stat; 
	char            *new_file, *lfile, *rfile, *file; 
	int		file_name_len; 
	int		bad_l_stat=0, bad_r_stat=0;
	int		lfile_is_dir, rfile_is_dir;
	char		*dir_file, *reg_file;
#ifdef WIN32
	char	*msgstr;
#endif WIN32

	lfile = *l_file; 
	rfile = *r_file;

	if (stat(lfile, &l_file_stat) == -1)
	    bad_l_stat = 1;

	if (stat(rfile, &r_file_stat) == -1)
	    bad_r_stat = 1;


	if (bad_l_stat && bad_r_stat)
	{							   
#ifndef WIN32
	    Msg_messageparams (2, lfile, rfile);
	    Msg_putmessage(toplevel,"k_both_bad_file_stats", 
				XtDisplay(toplevel), dxdiff_hierarchy);
#else
		msgstr = XtMalloc( strlen ("Unable to obtain file statistics for  and .") + 
								    strlen(rfile) + strlen(lfile) + 1);
		strcat (msgstr, "Unable to obtain file statistics for ");
		strcat (msgstr, lfile);
		strcat (msgstr, " and ");
		strcat (msgstr, rfile);
		strcat (msgstr, ".");
		MessageBox( NULL,				/* owner window handle */
					msgstr,
					MSGBOX_TITLE,
					MB_ICONEXCLAMATION || MB_SETFOREGROUND || MB_OK );
		XtFree(msgstr);
#endif WIN32
	    return BAD_STAT_BOTH;
	}
	else if (bad_l_stat)
	{
#ifndef WIN32
	    Msg_messageparams (1, lfile);
	    Msg_putmessage(toplevel,"k_bad_file_stats", 
				XtDisplay(toplevel), dxdiff_hierarchy);
#else
		msgstr = XtMalloc( strlen ("Unable to obtain file statistics for .") + 
								    strlen(lfile) + 1);
		strcat (msgstr, "Unable to obtain file statistics for ");
		strcat (msgstr, lfile);
		strcat (msgstr, ".");
		MessageBox( NULL,				/* owner window handle */
					msgstr,
					MSGBOX_TITLE,
					MB_ICONEXCLAMATION || MB_SETFOREGROUND || MB_OK );
		XtFree(msgstr);
#endif WIN32
	    return(BAD_STAT_L_FILE);
	}
	else if (bad_r_stat)
	{
#ifndef WIN32
	    Msg_messageparams (1, rfile);
	    Msg_putmessage(toplevel,"k_bad_file_stats", 
				XtDisplay(toplevel), dxdiff_hierarchy);
#else
		msgstr = XtMalloc( strlen ("Unable to obtain file statistics for .") + 
								    strlen(rfile) + 1);
		strcat (msgstr, "Unable to obtain file statistics for ");
		strcat (msgstr, rfile);
		strcat (msgstr, ".");
		MessageBox( NULL,				/* owner window handle */
					msgstr,
					MSGBOX_TITLE,
					MB_ICONEXCLAMATION || MB_SETFOREGROUND || MB_OK );
		XtFree(msgstr);
#endif WIN32
	    return(BAD_STAT_R_FILE);
	}

	lfile_is_dir = ((l_file_stat.st_mode & S_IFMT) == S_IFDIR);
	rfile_is_dir = ((r_file_stat.st_mode & S_IFMT) == S_IFDIR);

	/* dp: if both files are directories
	 */
	if (lfile_is_dir && rfile_is_dir)
	{
#ifndef WIN32
	    Msg_messageparams (2, lfile, rfile);
	    Msg_putmessage(toplevel,"k_both_dirs", 
					XtDisplay(toplevel), dxdiff_hierarchy);
#else
		msgstr = XtMalloc( strlen ("Files  and  are both directories.") + 
								    strlen(rfile) + strlen(lfile) + 1);
		strcat (msgstr, "Files ");
		strcat (msgstr, lfile);
		strcat (msgstr, " and ");
		strcat (msgstr, rfile);
		strcat (msgstr, " are both directories.");
		MessageBox( NULL,				/* owner window handle */
					msgstr,
					MSGBOX_TITLE,
					MB_ICONEXCLAMATION || MB_SETFOREGROUND || MB_OK );
		XtFree(msgstr);
#endif WIN32
	    return(BOTH_DIRS);
	}
	else if (lfile_is_dir || rfile_is_dir)
	{
	    /* dp:  if we are here, then one of the files is a regular
	     *	file and one is a directory. We append the name of the 
	     *	regular file to the directory.
	     */
	    if (lfile_is_dir)	/* lfile is a directory */
	    {
		dir_file = lfile;
		reg_file = rfile;
	    }
	    else		/* rfile is a directory */
	    {
		dir_file = rfile;
		reg_file = lfile;
	    }


	    /* dp:	append the last part of the file path
	     * (if there is a  full path !!) to the directory specification.
	     */
	    file_name_len = (file = strrchr(reg_file, '/')) ? 
					strlen(++file) : strlen(reg_file);

	    /* dp: if there is no trailing "/" add one !
	     */
	    if (dir_file[strlen(dir_file)-1] != '/')
	    {
		new_file = (char *) XtMalloc(strlen(dir_file) + 
							file_name_len + 2);
		strcpy(new_file, dir_file);
		strcat(new_file, "/");
	    }
	    else
	    {
		new_file = (char *) XtMalloc(strlen(dir_file) + 
							file_name_len + 1);
		strcpy(new_file, dir_file);
	    }

	    if (file)
		strcat(new_file, file);
	    else
		strcat(new_file, reg_file);


	    if (lfile_is_dir)
		*l_file = new_file;
	    else
		*r_file = new_file;


	}	/* end  one of the files is a directory file */

	return (SUCCESS);
}
