/*                        Copyright (c) 1988 Bellcore
**                            All Rights Reserved
**       Permission is granted to copy or use this program, EXCEPT that it
**       may not be sold for profit, the copyright notice must be reproduced
**       on copies, and credit should be given to Bellcore where it is due.
**       BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
*/


#ifndef lint
static char rcsid[]= "$Header: spiff.c,v 1.1 88/09/15 11:33:51 daniel Rel $";
#endif


#include <stdio.h>
#include "misc.h"
#include "flagdefs.h"
#include "parse.h"
#include "edit.h"
#include "line.h"
#include "token.h"
#include "tol.h"
#include "command.h"
#include "compare.h"
#include "exact.h"
#include "miller.h"
#include "visual.h"
#include "output.h"

extern void _Y_doargs();

static int _Y_eflag = 0;	/* use exact match algorithm */
static int _Y_vflag = 0;	/* use visual mode */

/*
**	this is the set of flags that gets used throughout the top module
**	as well as being used to communicate between modules.
*/
static int _Y_flags;

main(argc,argv)
int argc;
char *argv[];
{
	E_edit edit_end;
	char *filename[2];

	int max_d; 	/* max number of differences allowed */
	int i;		/* loop counter */

	/*
	**	parse the command line
	*/
	_Y_doargs(argc,argv,&(filename[0]),&(filename[1]),&max_d);

	/*
	**	initialize the default tolerance if it
	**		hasn't been set already.
	*/
	T_initdefault();

	/*
	**	read and then parse the files
	*/

	/*
	**	L_initfile return a code that indicates if the
	**	entire file was read or not
	**
	**	P_fileparse also knows how to start at someplace other
	**		than the first line of file
	**
	**	Taken together, this is enough to do step our way
	**		through the file using an exact match algorithm.
	**
	**	Oh well, someday . . .
	*/
	for(i=0;i<=1;i++)
	{
		/*
		**	read the file into core
		*/
		(void) L_init_file(i,filename[i]);
		K_settmax(i,0);		/* start tokens at 0 */
		/*
		**	and parse the files into tokens
		*/
		P_file_parse(i,0,L_getrlmax(i),_Y_flags);
	}

	if (_Y_vflag)
	{
		return(V_visual(_Y_flags));
	}

	/*
	**	if max_d was not set on the command line
	**		set it to be as large as is possible
	**		since the most changes possible would
	**		be to delete all the tokens in the
	**		first file and add all the tokens from
	**		the second, the max possible is the
	**		sum of the number of tokens in the
	**		two files.
	*/
	if (-1 == max_d)
		max_d = K_gettmax(0) + K_gettmax(1);

	if (_Y_eflag)
	{
		edit_end = Q_do_exact(K_gettmax(0),K_gettmax(1),
					max_d,_Y_flags);
	}
	else
	{
		edit_end = G_do_miller(K_gettmax(0), K_gettmax(1),
				     max_d,_Y_flags);
	}

	if (E_NULL != edit_end)
	{
		O_output(edit_end,_Y_flags);
		return(1);
	}
	return(0);
}

/*
**	break a string into individual lines and feed
**		them to the command module
*/
static void
_Y_cmdlines(from)
char *from;
{
	char buf[Z_LINELEN]; 
	char *to;
	while ('\0' != *from)
	{
		/*
		**	copy line into buf
		*/
		to = buf;
		while (('\0' != *from) && ('\n' != *from))
		{
			*to++ = *from++;
		}
		*to = '\0';	/* terminate the line */

		/*
		**	hand the line to the command module
		*/
		C_addcmd(buf);
		/*
		**	skip the newline
		*/
		if ('\n' == *from)
		{
			from++;
		}
	}
}

/*
**	this useful macro handle arguements that are adjacent
**	to a flag or in the following word e.g --
**
**		-a XXX 
**	and
**		-aXXX 
**
**	both work when SETPTR is used. 
*/
#define SETPTR	{if(strlen(argv[1]) == 2) {argv++;argc--;ptr=argv[1];}else ptr=(&argv[1][2]);}

static void
_Y_doargs(argc,argv,file1,file2,max_d)
int argc;
char *argv[];
char **file1,**file2;
int *max_d;
{
	char *ptr;
	extern char *fgets ();

	/*
	**	mark maximum number of tokens as being unset
	*/
	*max_d = -1;

	while (argc > 1 && argv[1][0] == '-')
	{
		switch (argv[1][1])
		{
			case 't':
				_Y_flags |= U_TOKENS;
				break;
			case 'w':
				_Y_flags |= U_INCLUDE_WS;
				break;

			case 'b':
				_Y_flags |= U_BYTE_COMPARE;
				break;

			case 'c':
				_Y_flags |= U_NO_CASE;
				break;
			case 'd' :
				_Y_flags |= U_NEED_DECIMAL;
				break;
			case 'm' :
				_Y_flags |= U_INC_SIGN;
				break;
			case 'a':
				SETPTR;
				T_defatol(ptr);
				break;
			case 'r':
				SETPTR;
				T_defrtol(ptr);
				break;
			case 'i':
				T_defitol();
				break;
			case 'e' :
				_Y_eflag = 1;
				break;
			case 'v' :
				_Y_vflag = 1;
				break;
			case 'q' :
				Z_setquiet();
				break;
			case 's' :
				SETPTR;
				_Y_cmdlines(ptr);
				break;
			case 'f' :
			{
				extern FILE *fopen();
				char buf[Z_LINELEN];
				FILE *cmdfile;
				SETPTR;
				if ((FILE*) NULL ==
					(cmdfile = fopen(ptr,"r")))
				{
					Z_fatal("can't open command file\n");
				}
				while ((char*) NULL !=
					(char*) fgets(buf,Z_LINELEN,cmdfile))
				{
					C_addcmd(buf);
				}
				(void) fclose(cmdfile);
				break;
			}
			/*
			**	useful commands for
			**	 the C programming language
			*/
			case 'C' :
				C_addcmd("literal  \"   \"    \\ ");
				C_addcmd("comment  /*  */	 ");
				C_addcmd("literal  &&		 ");
				C_addcmd("literal  ||		 ");
				C_addcmd("literal  <=		 ");
				C_addcmd("literal  >=		 ");
				C_addcmd("literal  !=		 ");
				C_addcmd("literal  ==		 ");
				C_addcmd("literal  --		 ");
				C_addcmd("literal  ++		 ");
				C_addcmd("literal  <<		 ");
				C_addcmd("literal  >>		 ");
				C_addcmd("literal  ->		 ");
				C_addcmd("addalpha _		 ");
				C_addcmd("tol      a0 		 ");
				break;
			/*
			**	useful commands for
			**	 the Bourne shell programming language
			*/
			case 'S' :
				C_addcmd("literal  '    '    \\	");
				C_addcmd("comment  #    $	");
				C_addcmd("tol      a0 		");
				break;
			/*
			**	useful commands for
			**	 the Fortran programming language
			*/
			case 'F' :
				C_addcmd("literal  '	'     ' ");
				C_addcmd("comment  ^C   $	");
				C_addcmd("tol      a0 		");
				break;
			/*
			**	useful commands for
			**	 the Lisp programming language
			*/
			case 'L' :
				C_addcmd("literal  \" 	\"	");
				C_addcmd("comment  ; 	$	");
				C_addcmd("tol      a0 		");
				break;
			/*
			**	useful commands for
			**	 the Modula-2 programming language
			*/
			case 'M' :
				C_addcmd("literal ' 	'	");
				C_addcmd("literal \"	\"	");
				C_addcmd("comment (*	*)	");
				C_addcmd("literal :=		");
				C_addcmd("literal <>		");
				C_addcmd("literal <=		");
				C_addcmd("literal >=		");
				C_addcmd("tol      a0 		");
				break;
			case '0':
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '8':
			case '9':
				*max_d = atoi(&argv[1][1]);
				break;
			default:
				Z_fatal("don't understand arguments\n");
		}
		++argv;
		--argc;
	}
	if (argc != 3)
		Z_fatal ("spiff requires two file names.\n");
	*file1 = argv[1];
	*file2 = argv[2];
}
