[comp.sources.unix] v16i069: Spiff, find approximate differences in files, Part03/04

rsalz@uunet.uu.net (Rich Salz) (11/12/88)

Submitted-by: Daniel W Nachbar <daniel@wind.bellcore.com>
Posting-number: Volume 16, Issue 69
Archive-name: spiff/part03

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 3 (of 4)."
# Contents:  float.c parse.c spiff.1
# Wrapped by rsalz@papaya.bbn.com on Fri Nov 11 13:12:26 1988
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'float.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'float.c'\"
else
echo shar: Extracting \"'float.c'\" \(13078 characters\)
sed "s/^X//" >'float.c' <<'END_OF_FILE'
X/*                        Copyright (c) 1988 Bellcore
X**                            All Rights Reserved
X**       Permission is granted to copy or use this program, EXCEPT that it
X**       may not be sold for profit, the copyright notice must be reproduced
X**       on copies, and credit should be given to Bellcore where it is due.
X**       BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
X*/
X
X
X#ifndef lint
Xstatic char rcsid[]= "$Header: float.c,v 1.1 88/09/15 11:33:53 daniel Rel $";
X#endif
X
X#include <ctype.h>
X#include "misc.h"
X#include "floatrep.h"
X#include "float.h"
X#include "strings.h"
X
X#define _F_GETEND(x)	(x + (strlen(x)-1)) 
X
X/*
Xint floatcnt = 0;
X*/
X/*
X**	routines to convert strings to our internal floating point form
X**		isfloat just looks at the string
X**		to see if a conversion is reasonable
X**			it does look-ahead on when it sees an 'e' and such.
X**		atocf actually does the conversion.
X**	these two routines could probably be combined
X*/
X
X/*
X**	test to see if the string can reasonably
X**		be interpreted as floating point number
X**	returns 0 if string can't be interpreted as a float
X**	otherwise returns the number of digits that will be used in F_atof
X*/
XF_isfloat(str,need_decimal,allow_sign)
Xchar *str;
Xint need_decimal;	/* if non-zero, require that a decimal point be present
X				otherwise, accept strings like "123" */
Xint allow_sign;		/* if non-zero, allow + or - to set the sign */
X{
X	int man_length = 0;	/* length of the fractional part (mantissa) */
X	int exp_length = 0;	/* length of the exponential part */
X	int got_a_digit = 0;	/* flag to set if we ever see a digit */
X
X	/*
X	**	look for an optional leading sign marker
X	*/
X	if (allow_sign && ('+' == *str  || '-' == *str))
X	{
X		str++; man_length++;
X	}
X	/*
X	**	count up the digits on the left hand side
X	**		 of the decimal point
X	*/
X	while(isdigit(*str))
X	{
X		got_a_digit = 1;
X		str++; man_length++;
X	}
X
X	/*
X	**	check for a decimal point
X	*/
X	if ('.' == *str)
X	{
X		str++; man_length++;
X	}
X	else
X	{
X		if (need_decimal)
X		{
X			return(0);
X		}
X	}
X
X	/*
X	**	collect the digits on the right hand
X	**		side of the decimal point
X	*/
X	while(isdigit(*str))
X	{
X		got_a_digit = 1;
X		str++; man_length++;
X	}
X
X	if (!got_a_digit)
X		return(0);
X
X	/*
X	**	now look ahead for an exponent
X	*/
X	if ('e' == *str ||
X	    'E' == *str ||
X	    'd' == *str ||
X	    'D' == *str)
X	{
X		str++; exp_length++;
X		if ('+' == *str  || '-' == *str)
X		{
X			str++; exp_length++;
X		}
X
X		if (!isdigit(*str))
X		{
X			/*
X			**	look ahead went too far,
X			**	so return just the length of the mantissa
X			*/
X			return(man_length);
X		}
X
X		while (isdigit(*str))
X		{
X			str++; exp_length++;
X		}
X	}
X	return(man_length+exp_length);	/* return the total length */
X}
X
X/*
X**	routine to convert a string to our internal
X**	floating point representation
X**
X**		similar to atof()
X*/
XF_float
XF_atof(str,allflag)
Xchar *str;
Xint allflag;	/* require that exactly all the characters are used */
X{
X	char *beg = str; /* place holder for beginning of the string */ 
X	char man[R_MANMAX];	/* temporary location to build the mantissa */
X	int length = 0;	/* length of the mantissa so far */
X	int got_a_digit = 0;	/* flag to set if we get a non-zero digit */ 
X	int i;
X	int resexp;
X
X	F_float res;	/* where we build the result */
X
X/*
Xfloatcnt++;
X*/
X	res = R_makefloat();
X
X	R_setsign(res,R_POSITIVE);
X
X	resexp = 0;
X	man[0] = '\0';
X
X	/*
X	**	check for leading sign
X	*/
X	if ('+' == *str)
X	{
X		/*
X		**	sign should already be positive, see above in this
X		**		routine, so just skip the plus sign
X		*/
X		str++;
X	}
X	else
X	{
X		if ('-' == *str)
X		{
X			R_setsign(res,R_NEGATIVE);
X			str++;
X		}
X	}
X
X	/*
X	**	skip any leading zeros
X	*/
X	while('0' == *str)
X	{
X		str++;
X	}
X
X	/*
X	**	now snarf up the digits on the left hand side
X	**		of the decimal point
X	*/
X	while(isdigit(*str))
X	{
X		got_a_digit = 1;
X		man[length++] = *str++;
X		man[length] = '\0';
X		resexp++;
X	}
X
X	/*
X	**	skip the decimal point if there is one
X	*/
X	if ('.' == *str)
X		str++;
X
X	/*
X	**	trim off any leading zeros (on the right hand side)
X	**	if there were no digits in front of the decimal point.
X	*/
X
X	if (!got_a_digit)
X	{
X		while('0' == *str)
X		{
X			str++;
X			resexp--;
X		}
X	}
X
X	/*
X	**	now snarf up the digits on the right hand side
X	*/
X	while(isdigit(*str))
X	{
X		man[length++] = *str++;
X		man[length] = '\0';
X	}
X
X	if ('e' == *str ||
X            'E' == *str ||
X            'd' == *str ||
X            'D' == *str )
X	{
X		str++;
X		resexp += atoi(str);
X	}
X
X	if (allflag)
X	{
X		if ('+' == *str ||
X		    '-' == *str)
X		{
X			str++;
X		}
X		while (isdigit(*str))
X		{
X			str++;
X		}
X		if ('\0' != *str)
X		{
X			(void) sprintf(Z_err_buf,
X					"didn't use up all of %s in atocf",
X					beg);
X			Z_fatal(Z_err_buf);
X		}
X	}
X
X	/*
X	**	check for special case of all zeros in the mantissa
X	*/
X	for (i=0;i<length;i++)
X	{
X		if (man[i] != '0')
X		{
X			/*
X			**	the mantissa is non-zero, so return it unchanged
X			*/
X			S_trimzeros(man);
X			/*
X			**	save a copy of the mantissa
X			*/
X			R_setfrac(res,man);
X			R_setexp(res,resexp);
X			return(res);
X		}
X	}
X
X	/*
X	**	the answer is 0, so . . .
X	*/
X	R_setzero(res);
X	return(res);
X}
X
X
X/*
X**	add s2 to s1
X*/
Xstatic
Xvoid
X_F_stradd(s1,s2)
Xchar *s1,*s2;
X{
X	char *end1 = s1 + (strlen(s1)-1);
X	char *end2 = s2 + (strlen(s2)-1);
X
X	static char result[R_MANMAX];
X	char *resptr = result+(R_MANMAX-1); /*point to the end of the array */
X	int carry = 0;
X	int tmp,val1,val2;
X
X	*resptr-- = '\0';
X
X	while ((end1 >= s1) ||  ( end2 >= s2))
X	{
X		if (end1 >= s1)
X		{
X			val1 = *end1 - '0';
X			--end1;
X		}
X		else
X		{
X			val1 = 0;
X		}
X
X		if (end2 >= s2)
X		{
X			val2 = *end2 - '0';
X			--end2;
X		}
X		else
X		{
X			val2 = 0;
X		}
X
X		tmp = val1 + val2 + carry;
X		if (tmp > 9)
X		{
X			carry = 1;
X			tmp -= 10;
X		}
X		else
X		{
X			carry = 0;
X		}
X
X		*resptr-- = tmp+'0';
X	}
X	if (carry)
X	{
X		*resptr =  '1';
X	}
X	else
X	{
X		resptr++;
X	}
X	(void) strcpy(s1,resptr);
X	return;
X}
X
X/*
X**	add zero(s) onto the end of a string
X*/
Xstatic void
Xaddzeros(ptr,count)
Xchar *ptr;
Xint count;
X{
X	for(;count> 0;count--)
X	{
X		(void) strcat(ptr,"0");
X	}
X	return;
X}
X
X/*
X**	subtract two mantissa strings
X*/
XF_float
XF_floatsub(p1,p2)
XF_float  p1,p2;
X{
X	static F_float result;
X	static needinit = 1;
X	static char man1[R_MANMAX],man2[R_MANMAX],diff[R_MANMAX];
X	int exp1,exp2;
X	char *diffptr,*big,*small;
X	int man_cmp_val,i,borrow;
X
X	if (needinit)
X	{
X		result = R_makefloat();
X		needinit = 0;
X	}
X
X	man1[0] = '\0';
X	man2[0] = '\0';
X
X	exp1 = R_getexp(p1);
X	exp2 = R_getexp(p2);
X
X	/*
X	**	line up the mantissas
X	*/
X	while (exp1 < exp2)
X	{
X		(void) strcat(man1,"0");
X		exp1++;
X	}
X
X	while(exp1 > exp2)
X	{
X		(void) strcat(man2,"0");
X		exp2++;
X	}
X
X	if (exp1 != exp2)	/* boiler plate assertion */
X	{
X		Z_fatal("mantissas didn't get lined up properly in floatsub");
X	}
X
X	(void) strcat(man1,R_getfrac(p1));
X	(void) strcat(man2,R_getfrac(p2));
X	
X	/*
X	**	now that the mantissa are aligned,
X	**	if the strings are the same, return 0
X	*/
X	if((man_cmp_val = strcmp(man1,man2)) == 0)
X	{
X		R_setzero(result);
X		return(result);
X	}
X
X	/*
X	**	pad the shorter string with 0's
X	**		when this loop finishes, both mantissas should
X	**		have the same length
X	*/
X	if (strlen(man1)> strlen(man2))
X	{
X		addzeros(man2,strlen(man1)-strlen(man2));
X	}
X	else
X	{
X		if (strlen(man1)<strlen(man2))
X		{
X			addzeros(man1,strlen(man2)-strlen(man1));
X		}
X	}
X
X	if (strlen(man1) != strlen(man2))	/* pure boilerplate */
X	{
X		Z_fatal("lengths not equal in F_floatsub");
X	}
X
X	if (man_cmp_val < 0)
X	{
X		big = man2;
X		small = man1;
X	}
X	else
X	{
X		big = man1;
X		small = man2;
X	}
X
X	/*
X	**	find the difference between the mantissas
X	*/
X	for(i=(strlen(big)-1),borrow=0,diff[strlen(big)] = '\0';i>=0;i--)
X	{
X		char from;
X		if (borrow)
X		{
X			if (big[i] == '0')
X			{
X				from = '9';
X			}
X			else
X			{
X				from = big[i]-1;
X				borrow = 0;
X			}
X		}
X		else
X		{
X			if(big[i]<small[i])
X			{
X				from = '9'+1;
X				borrow = 1;
X			}
X			else
X			{
X				from = big[i];
X			}
X		}
X		diff[i] = (from-small[i]) + '0';
X	}
X
X	/*
X	** trim the leading zeros on the difference
X	*/
X	diffptr = diff;
X	while('0' == *diffptr)
X	{
X		diffptr++;
X		exp1--;
X	}
X
X	R_setexp(result,exp1); /* exponents are equal at the point */
X	R_setfrac(result,diffptr);
X	R_setsign(result,R_POSITIVE);
X	return(result);
X}
X
XF_floatcmp(f1,f2)
XF_float f1,f2;
X{
X	static char man1[R_MANMAX],man2[R_MANMAX];
X
X	/*
X	**		special case for zero
X	*/
X	if (R_zerofloat(f1))
X	{
X		if (R_zerofloat(f2))
X		{
X			return(0);
X		}
X		else
X		{
X			return(-1);
X		}
X	}
X	else
X	{
X		if (R_zerofloat(f2))
X		{
X			return(1);
X		}
X	}
X
X	/*
X	**	to reach this point, both numbers must be non zeros
X	*/
X	if (R_getexp(f1) < R_getexp(f2))
X	{
X		return(-1);
X	}
X
X	if (R_getexp(f1) > R_getexp(f2))
X	{
X		return(1);
X	}
X
X	(void) strcpy(man1,R_getfrac(f1));
X	S_trimzeros(man1);
X
X	(void) strcpy(man2,R_getfrac(f2));
X	S_trimzeros(man2);
X	return(strcmp(man1,man2));
X}
X
XF_float
XF_floatmul(f1,f2)
XF_float f1,f2;
X{
X	static char prod[R_MANMAX];
X	char *end;
X	int count1 = 0;
X	int count2 = 0;
X	int tmp,len;
X	char *end1;
X	char *end2;
X	static char man1[R_MANMAX],man2[R_MANMAX];
X	char *bigman,*smallman;
X	static F_float result;
X	static int needinit = 1;
X
X	if (needinit)
X	{
X		result = R_makefloat();
X		needinit = 0;
X	}
X	/*
X	**	special case for a zero result
X	*/
X	if (R_zerofloat(f1) || R_zerofloat(f2))
X	{
X		R_setzero(result);
X		return(result);
X	}
X
X	(void) strcpy(man1,R_getfrac(f1));
X	(void) strcpy(man2,R_getfrac(f2));
X
X	end1 = _F_GETEND(man1);
X	end2 = _F_GETEND(man2);
X
X	/*
X	**	decide which number will cause multiplication loop to go
X	**	around the least
X	*/
X	while(end1 >= man1)
X	{
X		count1 += *end1 - '0';
X		end1--;
X	}
X
X	while(end2 >= man2)
X	{
X		count2 += *end2 - '0';
X		end2--;
X	}
X
X
X	if (count1 > count2)
X	{
X		bigman = man1;
X		smallman = man2;
X	}
X	else
X	{
X		bigman = man2;
X		smallman = man1;
X	}
X	S_trimzeros(bigman);
X	S_trimzeros(smallman);
X	len = strlen(bigman) +  strlen(smallman);
X
X	end = _F_GETEND(smallman);
X	(void) strcpy(prod,"0");
X
X	/*
X	**	multiplication by repeated addition
X	*/
X	while(end >= smallman)
X	{
X		for(tmp = 0;tmp<*end-'0';tmp++)
X		{
X			_F_stradd(prod,bigman);
X		}
X		addzeros(bigman,1);
X		end--;
X	}
X
X	R_setfrac(result,prod);
X	R_setexp(result,(((R_getexp(f1) + R_getexp(f2)) - len)+ strlen(prod)));
X
X	if (R_getsign(f1) == R_getsign(f2))
X	{
X		R_setsign(result,R_POSITIVE);
X	}
X	else
X	{
X		R_setsign(result,R_NEGATIVE);
X	}
X	return(result);
X}
X
X_F_xor(x,y)
X{
X	return(((x) && !(y)) || (!(x) && (y)));
X}
X#define	_F_SAMESIGN(x,y)	_F_xor((x<0),(y<0))
X#define _F_ABSADD(x,y)		(Z_ABS(x) + Z_ABS(y))
X
X_F_ABSDIFF(x,y)
X{
X	if (Z_ABS(x) < Z_ABS(y))
X	{
X		return(Z_ABS(y) - Z_ABS(x));
X	}
X	else
X	{
X		return(Z_ABS(x) - Z_ABS(y));
X	}
X}
X/*
X**	add two floats without regard to sign
X*/
XF_float
XF_floatmagadd(p1,p2)
XF_float p1,p2;
X{
X	static F_float result;
X	static int needinit = 1;
X
X	static char  man1[R_MANMAX],man2[R_MANMAX];
X
X	int digits;	/* count of the number of digits needed to represent the
X				result */
X	int resexp;	/* exponent of the result */
X	int len;	/* length of the elements before adding */
X	char *diffptr;
X
X	if (needinit)
X	{
X		result = R_makefloat();
X		needinit = 0;
X	}
X	(void) strcpy(man1,"");
X	(void) strcpy(man2,"");
X
X	/*
X	**	find the difference in the exponents number of digits
X	*/
X	if( _F_SAMESIGN(R_getexp(p1),R_getexp(p2)))
X	{
X		digits =  _F_ABSDIFF(R_getexp(p1),R_getexp(p2));
X	}
X	else
X	{
X		digits = _F_ABSADD(R_getexp(p1),R_getexp(p2));
X	}
X
X	/*
X	**	make sure that there is room to store the result
X	*/
X	if (digits>0)
X	{ 
X		if (R_getexp(p1) < R_getexp(p2))
X		{
X			/*
X			**	leave room for terminator
X			*/
X			if (digits+strlen(R_getfrac(p1)) > (R_MANMAX-1))
X			{
X				(void) sprintf(Z_err_buf,
X				   "numbers differ by too much in magnitude");
X				Z_fatal(Z_err_buf);
X			}
X		}
X		else
X		{
X			/*
X			**	leave room for terminator
X			*/
X			if (digits+strlen(R_getfrac(p2)) > (R_MANMAX-1))
X			{
X				(void) sprintf(Z_err_buf,
X				   "numbers differ by too much in magnitude");
X				Z_fatal(Z_err_buf);
X			}
X		}
X	}
X	else
X	{
X		/*
X		**	leave room for terminator and possible carry
X		*/
X		if (Z_MAX(strlen(R_getfrac(p1)),
X			strlen(R_getfrac(p2))) > (R_MANMAX-2))
X		{						
X			(void) sprintf(Z_err_buf,
X			   "numbers differ by too much in magnitude");
X			Z_fatal(Z_err_buf);
X		}
X	}
X
X	/*
X	**	pad zeroes on the front of the smaller number
X	*/
X	if (R_getexp(p1) < R_getexp(p2))
X	{
X
X		addzeros(man1,digits);
X		resexp = R_getexp(p2);
X	}
X	else
X	{
X		addzeros(man2,digits);
X		resexp = R_getexp(p1);
X	}
X	(void) strcat(man1,R_getfrac(p1));
X	(void) strcat(man2,R_getfrac(p2));
X
X	len = Z_MAX(strlen(man1),strlen(man2));
X
X	/*
X	**	add the two values
X	*/
X	_F_stradd(man1,man2);
X
X	/*
X	**	adjust the exponent to account for a
X	**		possible carry
X	*/
X	resexp += strlen(man1) - len;
X
X
X	/*
X	** trim the leading zeros on the sum
X	*/
X	diffptr = man1;
X	while('0' == *diffptr)
X	{
X		diffptr++;
X		resexp--;
X	}
X
X	R_setfrac(result,diffptr);
X	R_setexp(result,resexp);
X	R_setsign(result,R_POSITIVE);
X
X	return(result);
X}
X
X/*
X**	useful debugging routine. we don't call it in the release,
X**	so it is commented out, but we'll leave it for future use
X*/
X
X/*
XF_printfloat(fl)
XF_float fl;
X{
X	(void) printf("fraction = :%s: exp = %d sign = %c\n",
X			R_getfrac(fl),
X			R_getexp(fl),
X			((R_getsign(fl) == R_POSITIVE) ? '+': '-'));
X
X}
X*/
END_OF_FILE
if test 13078 -ne `wc -c <'float.c'`; then
    echo shar: \"'float.c'\" unpacked with wrong size!
fi
# end of 'float.c'
fi
if test -f 'parse.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'parse.c'\"
else
echo shar: Extracting \"'parse.c'\" \(16604 characters\)
sed "s/^X//" >'parse.c' <<'END_OF_FILE'
X/*                        Copyright (c) 1988 Bellcore
X**                            All Rights Reserved
X**       Permission is granted to copy or use this program, EXCEPT that it
X**       may not be sold for profit, the copyright notice must be reproduced
X**       on copies, and credit should be given to Bellcore where it is due.
X**       BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
X*/
X
X
X#ifndef lint
Xstatic char rcsid[]= "$Header: parse.c,v 1.1 88/09/15 11:33:57 daniel Rel $";
X#endif
X
X#include "misc.h"
X#include "flagdefs.h"
X#include "float.h"
X#include "tol.h"
X#include "token.h"
X#include "line.h"
X#include "command.h"
X#include "comment.h"
X#include "parse.h"
X
X
X#include <ctype.h>
X
X#define _P_PARSE_CHATTER	1000
X
X
Xstatic	int _P_realline;	/* loop counter */
Xstatic  int _P_fnumb;
X
Xstatic  char *_P_nextchr;	/* pointer to the next character to parse */
Xstatic	char *_P_firstchr;		/* pointer to the beginning of the line being parsed */
Xstatic	int _P_next_tol;		/* number of floats seen on this line */
Xstatic	int _P_stringsize;		/* count of number of characters that are being
X					read into a comment or literal */
Xstatic int _P_has_content;	/* flag to indicate if the line being
X					parsed has any tokens on it */
Xstatic int _P_start;		/* first line to parse */
Xstatic int _P_lcount;		/* number of lines to parse */
X
Xstatic int _P_flags;		/* location for global flags */
X
X/*
X**	by default, "words" can be made up of numbers and letters
X**	the following code allows for extending the alphabet that can
X**	be used in words. this is useful for handling languages such
X**	as C where the underscore character is an allowable character
X**	in an identifier.  If a character (such as underscore) is NOT added
X**	to the alphabet, the identifier will be broken into 2 or more "words"
X**	by the parser.  as such the two sequences
X**			one_two
X**		and
X**			one _ two
X**	would look identical to spiff.
X*/
X#define _P_ALPHALEN 256
Xstatic char _P_alpha[_P_ALPHALEN];
X
Xstatic void
X_P_alpha_clear()
X{
X	*_P_alpha = '\0';
X}
X
Xstatic
X_P_in_alpha(chr)
Xchar chr;
X{
X#ifndef ATT
X	extern int index();
X#endif
X	/*
X	**	special case when string terminator
X	**	is handed to us
X	*/
X	if ('\0' == chr)
X		return(0);
X
X#ifdef ATT
X	return((int) strchr(_P_alpha,chr));
X#else
X	return((int) index(_P_alpha,chr));
X#endif
X}
X
Xvoid
XP_addalpha(ptr)
Xchar *ptr;
X{
X	char buf[Z_LINELEN];
X
X	S_wordcpy(buf,ptr);		/* copy up to (but not including)
X						the first whitespace char */
X
X	if ((strlen(_P_alpha) + strlen(buf)) >= _P_ALPHALEN)
X	{
X		Z_fatal("too many characters added to extended alphabet");
X	}
X	(void) strcat(_P_alpha,buf);
X}
X
X/*
X**	put parser in a default state
X*/
X
Xstatic char _P_dummyline[2];	/* a place to aim wild pointers */
Xstatic void
X_P_initparser()
X{
X	_P_dummyline[0] = '\0';
X
X	/*
X	**	now reset all the state of each module
X	*/
X	C_clear_cmd();		/* disable embedded command key word */ 
X	T_clear_tols();
X	W_clearcoms();
X	W_clearlits();
X	_P_alpha_clear();	/* disable extended alphabet */
X
X	/*
X	**	and set state as defined by execute-time commands.
X	*/
X	C_docmds();
X	return;
X}
X
X
Xstatic
X_P_needmore()
X{
X	return(*_P_nextchr == '\0');
X}
X
Xstatic
X_P_nextline()
X{
X	/*
X	**	if the line that we just finished had
X	**		some content,  increment the count
X	*/
X	if (_P_has_content)
X	{
X		L_incclmax(_P_fnumb);
X		/*
X		**	if the previous line had a token
X		**		increment the line
X		*/
X		if (L_getcount(_P_fnumb,L_gettlmax(_P_fnumb)))
X		{
X			L_inctlmax(_P_fnumb);
X			L_setcount(_P_fnumb,L_gettlmax(_P_fnumb),0);
X		}
X		_P_has_content = 0;
X	}
X
X	/*
X	**	reset the number of floats seen on the line
X	*/
X	_P_next_tol = 0;
X
X	/*
X	**	get another line if there is one available
X	*/
X	_P_realline++;
X	if (_P_realline >= _P_start+_P_lcount)
X	{
X		return(1);
X	}
X
X	_P_firstchr = _P_nextchr = L_getrline(_P_fnumb,_P_realline);
X	/*
X	**	and look for a command
X	*/
X	if (C_is_cmd(_P_firstchr))
X	{
X		_P_nextchr = _P_dummyline;
X		_P_has_content = 0;
X	}
X	else
X	{
X		/*
X		**	we have a real line, so set up the index
X		*/
X		L_setclindex(_P_fnumb,L_getclmax(_P_fnumb),_P_realline);
X		_P_has_content = 1;
X	}
X	return(0);
X}
X
X/*
X**	the following three routines (_P_litsnarf, _P_bolsnarf, and _P_comsnarf
X**	all do roughly the same thing. they scan ahead and collect the
X**	specified string, move _P_nextchr to the end of the
X**	comment or literal and return 1 if we run off the end of file,
X**	0 otherwise.  it would have been nice to have 1 routine handle
X**	all three task (there is much common code), however there were
X**	so enough differences, (for instance, only comments check for nesting,
X**	only literals need to set _P_stringsize, etc)
X**	that I decided to split them up.
X*/
Xstatic int
X_P_litsnarf(litptr)
XW_lit litptr; 
X{
X	_P_stringsize = 0;
X	/*
X	**	skip the start of literal string
X	*/
X	_P_nextchr += strlen(W_litbegin(litptr));
X	_P_stringsize += strlen(W_litbegin(litptr));
X	/*
X	**	is there a separate end string?
X	**		if not, then we're done
X	*/
X	if ('\0' == *(W_litend(litptr)))
X	{
X		return(0);
X	}
X	/*
X	**	loop once for each character in the literal
X	*/
X	while(1)
X	{
X		/*
X		**	if we are out of characters, move on to next line
X		*/
X		if (_P_needmore())
X		{
X			if (_P_nextline())
X			{
X				return(1);
X			}
X			if (!_P_has_content)
X			{
X				/*
X				**	since we've just gotten a command
X				**		check to see if this literal
X				**		is still legit ...
X				**		could have just been reset
X				**		by the command
X				*/
X				if (!W_is_lit(litptr))
X				{
X					return(0);
X				}
X			}
X		} /* if _P_needmore */
X
X		/*
X		**	see if we have an escaped end of literal string
X		*/
X		if (('\0' != *(W_litescape(litptr))) && /* escape string exists */
X		  !S_wordcmp(_P_nextchr,
X			   W_litescape(litptr)) &&     /* and escape matches */
X		  !S_wordcmp(_P_nextchr+strlen(W_litescape(litptr)),
X			   W_litend(litptr)))	     /* and endstring matches */
X		{
X			_P_nextchr += strlen(W_litescape(litptr))
X					+ strlen(W_litend(litptr));
X			_P_stringsize += strlen(W_litescape(litptr))
X					+ strlen(W_litend(litptr));
X			continue;
X		}
X
X		/*
X		**	see if we have an end of literal string
X		*/
X		if (!S_wordcmp(_P_nextchr,W_litend(litptr))) /* escape matches */
X		{
X			_P_nextchr += strlen(W_litend(litptr));
X			_P_stringsize += strlen(W_litend(litptr));
X			return(0);
X		}
X		/*
X		**	this must be yet another character in the literal, so
X		**	just snarf it up
X		*/
X		_P_nextchr++;
X		_P_stringsize++;
X	}	/* while loop once for each character */
X
X#ifndef lint
X	Z_fatal("shouldn't execute this line at the end of _P_litsnarf");
X#endif
X} /* _P_litsnarf */
X
Xstatic int
X_P_bolsnarf(bolptr)
XW_bol bolptr; 
X{
X	/*
X	**	skip the start of comment string
X	*/
X	_P_nextchr += strlen(W_bolbegin(bolptr));
X	/*
X	**	is there a separate end string
X	**		if not, then we're done
X	*/
X	if ('\0' == *(W_bolend(bolptr)))
X	{
X		return(0);
X	}
X	/*
X	**	loop once for each character in the comment
X	*/
X	while(1)
X	{
X		/*
X		**	if we are out of characters,move on to next line
X		*/
X		if (_P_needmore())
X		{
X			if (_P_nextline())
X			{
X				return(1);
X			}
X			if (!_P_has_content)
X			{
X				/*
X				**	since we've just gotten a command
X				**		check to see if this comment
X				**		is still legit ... comments
X				**		could have just been reset
X				**		by the command
X				*/
X				if (!W_is_bol(bolptr))
X				{
X					return(0);
X				}
X			}
X		} /* if at end of line */
X
X		/*
X		**	see if we have an escaped end of comment string
X		*/
X		if ('\0' != *(W_bolescape(bolptr)) && /* escape string exists */
X		  !S_wordcmp(_P_nextchr,
X			   W_bolescape(bolptr)) &&     /* and escape matches */
X		  !S_wordcmp(_P_nextchr+strlen(W_bolescape(bolptr)),
X			   W_bolend(bolptr)))	 /* and end string matches */
X		{
X			_P_nextchr += strlen(W_bolescape(bolptr))
X					+ strlen(W_bolend(bolptr));
X			continue;
X		}
X
X		/*
X		**	see if we have an end of comment string
X		*/
X		if (!S_wordcmp(_P_nextchr,W_bolend(bolptr)))
X		{
X			_P_nextchr += strlen(W_bolend(bolptr));
X			return(0);
X		}
X		/*
X		**	this must be yet another character in the comment, so
X		**	just snarf it up
X		*/
X		_P_nextchr++;
X	}	/* while loop once for each character */
X
X#ifndef lint
X	Z_fatal("shouldn't execute this line in at end of _P_bolsnarf");
X#endif
X} /* _P_bolsnarf */
X
X/*
X**	pass over a comment -- look for nexting
X*/
Xstatic
X_P_comsnarf(comptr)
XW_com comptr; 
X{
X	int depth = 1; /* nesting depth */
X	/*
X	**	skip the start of comment string
X	*/
X	_P_nextchr += strlen(W_combegin(comptr));
X
X	/*
X	**	is there a separate end string
X	**		if not, then we're done
X	*/
X	if ('\0' == *(W_comend(comptr)))
X	{
X		return(0);
X	}
X	/*
X	**	loop once for each character in the comment
X	*/
X	while(1)
X	{
X		/*
X		**	if we are out of characters, move on to next line
X		*/
X		if (_P_needmore())
X		{
X			if (_P_nextline())
X			{
X				return(1);
X			}
X			if (!_P_has_content)
X			{
X				/*
X				**	since we've just gotten a command
X				**		check to see if this comment
X				**		is still legit ... comments
X				**		could have just been reset
X				**		by the command
X				*/
X				if (!W_is_com(comptr))
X				{
X					return(0);
X				}
X			}
X		} /* if at end of line */
X
X		/*
X		**	see if we have an escaped end of comment string
X		*/
X		if ('\0' != *(W_comescape(comptr)) &&  /* escape string exists */
X		  !S_wordcmp(_P_nextchr,
X			   W_comescape(comptr)) &&    /* and escape matches */
X		  !S_wordcmp(_P_nextchr+strlen(W_comescape(comptr)),
X			   W_comend(comptr)))	/* and end string matches */
X		{
X			/*
X			** skip over the escape sequence and the end sequence
X			*/
X			_P_nextchr += strlen(W_comescape(comptr))
X					+ strlen(W_comend(comptr));
X			continue;
X		}
X
X		/*
X		**	see if we have an end of comment string
X		*/
X		if (!S_wordcmp(_P_nextchr,W_comend(comptr))) /* end  matches */
X		{
X			/*
X			**	skip over the end sequence
X			*/
X			_P_nextchr += strlen(W_comend(comptr));
X			if (W_is_nesting(comptr))
X			{
X				depth--;
X				if (0 == depth)
X					return(0);
X			}
X			else
X			{
X				return(0);
X			}
X			continue;
X		}
X		/*
X		**	see if we have another beginning of comment string
X		*/
X		if (W_is_nesting(comptr) &&
X			!S_wordcmp(_P_nextchr,W_comend(comptr))) /* end matches */
X		{
X			_P_nextchr += strlen(W_comend(comptr));
X			depth++;
X			continue;
X		}
X		/*
X		**	this must be yet another character in the comment, so
X		**	just snarf it up
X		*/
X		_P_nextchr++;
X	}	/* while loop once for each character */
X
X#ifndef lint
X		Z_fatal("should not execute this line in _P_comsnarf\n");
X#endif
X
X} /* _P_comsnarf */
X
X
X/*
X**	parse a file
X*/
Xstatic void
X_P_do_parse()
X{
X
X	char *ptr;		/* scratch space */
X	int tmp;
X	int ret_code;
X
X	K_token newtoken;
X	W_bol bolptr;
X	W_com comptr;
X	W_lit litptr;
X
X	int startline, endline, startpos;
X
X	/*
X	**	main parsing loop
X	*/
X	while (1)
X	{
X		/*
X		**	get more text if necessary
X		*/
X		if (_P_needmore())
X		{
X			if (_P_nextline())
X			{
X				return;
X			}
X
X			/*
X			**	if the line contains nothing of interest,
X			**		try again
X			*/
X			if (!_P_has_content)
X			{
X				continue;
X			}
X
X			/*
X			**	check to see if this line starts a comment
X			*/
X			if ((bolptr = W_isbol(_P_firstchr)) != W_BOLNULL)
X			{
X				if (_P_bolsnarf(bolptr))
X				{
X					return;
X				}
X				continue;
X			}
X		} /* if _P_needmore */
X
X		/*
X		**	skip whitespace
X		*/
X		if (!(U_INCLUDE_WS & _P_flags) && isspace(*_P_nextchr))
X		{
X			_P_nextchr++;
X			continue;
X		}
X
X		/*
X		**	check to see if this character starts a comment
X		*/
X		if ((comptr = W_iscom(_P_nextchr)) != W_COMNULL)
X		{
X			if (_P_comsnarf(comptr))
X			{
X				return;
X			}
X			continue;
X		}
X
X		/*
X		**	if there aren't any tokens on this line already
X		**	set up the index from the token line to the content line
X		*/
X		if (!L_getcount(_P_fnumb,L_gettlmax(_P_fnumb)))
X		{
X			L_settlindex(_P_fnumb,
X					L_gettlmax(_P_fnumb),
X					L_getclmax(_P_fnumb));
X			/*
X			**	and the pointer from the token line to the 
X			** 	first  token on the line
X			*/
X			L_setindex(_P_fnumb,
X					L_gettlmax(_P_fnumb),
X					K_gettmax(_P_fnumb));
X		}
X
X		startline =  L_tl2cl(_P_fnumb,L_gettlmax(_P_fnumb));
X		startpos = _P_nextchr-_P_firstchr;
X
X		newtoken = K_maketoken();
X		K_setline(newtoken,L_gettlmax(_P_fnumb));
X		K_setpos(newtoken,startpos);
X
X		ret_code = 0;
X		/*
X		**	check to see if this character starts a
X		**		delimited literal string
X		*/
X		if ((litptr = W_islit(_P_nextchr)) != W_LITNULL)
X		{
X			ret_code = _P_litsnarf(litptr);
X			K_settype(newtoken,K_LIT);
X			S_allocstr(&ptr,_P_stringsize);
X			/*
X			**	fixed nasty memory bug here by adding else
X			**	old code copied entire line even if literal
X			**	ended before the end of line
X			**		should check into getting strcpy loaded
X			**		locally
X			*/
X			endline = L_getclmax(_P_fnumb);
X			if (endline > startline)
X			{
X				/*
X				**	copy in the first line of the literal
X				*/
X				(void) strcpy(ptr,
X					      L_getcline(_P_fnumb,startline)
X							+startpos);
X				/*
X				**	now copy all the lines between
X				**		the first and last
X				*/
X				for (tmp=startline+1;tmp<endline;tmp++)
X				{
X					(void) strcat(ptr,
X						      L_getcline(_P_fnumb,tmp));
X				}
X				/*
X				**	and now copy in the last line
X				*/
X				(void) strncat(ptr,
X					       L_getcline(_P_fnumb,endline),
X					       _P_stringsize-strlen(ptr));
X			}
X			else
X			{
X				(void) strncpy(ptr,
X					       L_getcline(_P_fnumb,startline)
X								+startpos,
X					      _P_stringsize);
X				/*
X				**	terminate the string you just copied
X				*/
X				ptr[_P_stringsize] = '\0';
X			}
X			K_settext(newtoken,ptr);
X		} /* if is_lit */
X
X		/*
X		**	see if this is a floating point number
X		*/
X		else if (tmp = F_isfloat(_P_nextchr,
X				       _P_flags & U_NEED_DECIMAL,
X				       _P_flags & U_INC_SIGN))
X		{
X			K_saventext(newtoken,_P_nextchr,tmp);
X			K_settype(newtoken,K_FLO_NUM);
X			if (!(_P_flags & U_BYTE_COMPARE))
X			{
X				K_setfloat(newtoken,
X					   F_atof(K_gettext(newtoken),
X					   USE_ALL));
X
X				/*
X				**	assign the curent tolerance
X				*/
X				K_settol(newtoken,T_gettol(_P_next_tol));
X			}
X
X			/*
X			**	use next tolerance in the
X			**		specification if there is one
X			*/
X			if (T_moretols(_P_next_tol))
X			{
X				_P_next_tol++;
X			}
X			/*
X			**	and move pointer past the float
X			*/
X			_P_nextchr += tmp;
X		}
X
X		/*
X		**	is this a fixed point number
X		*/
X		else if (isdigit(*_P_nextchr))
X		{
X			for(ptr=_P_nextchr; isdigit(*ptr); ptr++)
X			{
X			}
X			K_saventext(newtoken,_P_nextchr,ptr-_P_nextchr);
X			K_settype(newtoken,K_LIT);
X			_P_nextchr = ptr;
X		}
X
X		/*
X		**	try an alpha-numeric word
X		*/
X		else if (isalpha(*_P_nextchr) || _P_in_alpha(*_P_nextchr))
X		{
X			/*
X			**	it's a multi character word
X			*/
X			for(ptr = _P_nextchr;
X			    isalpha(*ptr)
X				|| isdigit(*ptr)
X				|| _P_in_alpha(*ptr);
X			    ptr++)
X			{
X			}
X			K_saventext(newtoken,_P_nextchr,ptr-_P_nextchr);
X			K_settype(newtoken,K_LIT);
X			_P_nextchr = ptr;
X		}
X		else
X		{
X			/*
X			**	otherwise, treat the char itself as a token
X			*/
X			K_saventext(newtoken,_P_nextchr,1);
X			K_settype(newtoken,K_LIT);
X			_P_nextchr++;
X		}
X
X		K_settoken(_P_fnumb,K_gettmax(_P_fnumb),newtoken);
X		L_inccount(_P_fnumb,L_gettlmax(_P_fnumb));
X		/*
X		**	if we are out of space, complain and quit
X		*/
X		if (K_inctmax(_P_fnumb))
X		{
X			(void) sprintf(Z_err_buf,
X     "warning -- to many tokens in file only first %d tokens will be used.\n",
X				       K_MAXTOKENS);
X			Z_complain(Z_err_buf);
X			return;
X		}
X#ifndef NOCHATTER
X		if (0 == (K_gettmax(_P_fnumb) % _P_PARSE_CHATTER))
X		{
X			int max = K_gettmax(_P_fnumb);
X			(void) sprintf(Z_err_buf,
X				"scanned %d words from file #%d\n",
X					max,_P_fnumb+1);
X			Z_chatter(Z_err_buf);
X		}
X#endif
X
X		/*
X		**	are we done?
X		*/
X		if(ret_code)
X		{
X			return;
X		}
X	}   /* loop once per object on a line */
X
X#ifndef lint 
X	Z_fatal("this line should never execute");
X#endif
X}
X
Xvoid
XP_file_parse(num,strt,lcnt,flags)
Xint num;	/* file number */
Xint strt;	/* first line to parse expressed in real line numbers */
Xint lcnt;	/* max number of lines to parse */
Xint flags;	/* flags for controlling the parse mode */
X{
X	/*
X	**	set module-wide state variables
X	*/
X	_P_fnumb = num;		
X	_P_start = strt;	
X	_P_lcount = lcnt;
X	_P_flags = flags;
X
X	_P_initparser();
X
X	_P_nextchr = _P_dummyline;
X
X	_P_has_content = 0;
X	_P_next_tol = 0;
X	L_setcount(_P_fnumb,L_gettlmax(_P_fnumb),0);
X	/*
X	**	start everything back one line (it will be incremented
X	**		just before the first line is accessed
X	*/
X	_P_realline = _P_start-1;
X
X	_P_do_parse();
X
X	/*
X	**	if the last line had content, increment the count
X	*/
X	if (_P_has_content)
X	{
X/*
X**	this code will get executed if we stopped parsing in the middle
X**	of a line.  i haven't looked at this case carefully.
X**	so, there is a good chance that it is buggy.
X*/
X(void) sprintf(Z_err_buf,"parser got confused at end of file\n");
XZ_complain(Z_err_buf);
X		L_incclmax(_P_fnumb);
X		if (L_getcount(_P_fnumb,L_gettlmax(_P_fnumb)))
X			L_inctlmax(_P_fnumb);
X	}
X	return;
X}
END_OF_FILE
if test 16604 -ne `wc -c <'parse.c'`; then
    echo shar: \"'parse.c'\" unpacked with wrong size!
fi
# end of 'parse.c'
fi
if test -f 'spiff.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'spiff.1'\"
else
echo shar: Extracting \"'spiff.1'\" \(14738 characters\)
sed "s/^X//" >'spiff.1' <<'END_OF_FILE'
X.ll 6i
X.pl 10.5i
X.po 1.25i
X.\"	@(#)spiff.1	1.0 (Bellcore) 9/20/87
X.\"
X.lt 6.0i
X.TH SPIFF 1 "February 2, 1988"
X.AT 3
X.SH NAME
Xspiff \- make controlled approximate comparisons between files 
X.SH SYNOPSIS
X.B spiff
X[
X.B \-s
Xscript ] [
X.B \-f
Xsfile ] [
X.B \-bteviqcdwm
X] [
X.B \-a
X\(br
X.B \-r
Xvalue ] \-value file1 file2
X.SH DESCRIPTION
X.I Spiff
Xcompares the contents of 
X.B file1
Xand
X.B file2
Xand prints a description of the important differences between
Xthe files.
XWhite space is ignored except to separate other objects.
X.I Spiff
Xmaintains tolerances below which differences between two floating point
Xnumbers are ignored. 
XDifferences in floating point notation (such as 3.4 3.40 and 3.4e01)
Xare treated as unimportant.
XUser specified delimited strings (i.e. comments) can also be ignored.
XInside other user specified delimited strings
X(i.e. quoted strings) whitespace can be significant.
X.PP
X.I Spiff's
Xoperation can be altered via command line options, a command script, and with
Xcommands that are embedded in the input files.
X.PP
XThe following options affect
X.I spiff's
Xoverall operation.
X.TP
X.B \-q
Xsuppresses warning messages.
X.TP
X.B \-v
Xuse a visually oriented display.  Works only in MGR windows.
X.PP
X.I Spiff
Xhas several flags to aid differencing of various programming languages.
XSee EXAMPLES for a detailed description of the effects of these flags.
X.TP
X.B \-C
Xtreat the input files as C program source code.
X.TP
X.B \-S
Xtreat the input files as Bourne shell program source code.
X.TP
X.B \-F
Xtreat the input files as Fortran program source code.
X.TP
X.B \-M
Xtreat the input files as Modula-2 program source code.
X.TP
X.B \-L
Xtreat the input files as Lisp program source code.
X.PP
XBy default, the output looks somewhat similar in appearance
Xto the output of diff(1).  Lines with differences are printed with
Xthe differences highlighted.  If stdout is a terminal, as determined
Xby isatty(), then highlighting uses standout mode as determined by termcap.
XIf stdout is not a tty, then the underlining (via underscore/backspace/char)
Xis used to highlight differences.
XThe following option can control the format of the ouput.
X.TP
X.B \-t
Xproduce output in terms of individual tokens.  This option is
Xmost useful for debugging as the output produced is verbose to
Xthe point of being unreadable.
X.PP
XThe following option controls the differencing algorithm.
X.TP
X.B \-e
Xcompare each token 
Xin the files with the object in the same ordinal
Xposition in the other file.  If the files have a different number
Xof objects, a warning message is printed
Xand the objects at the end of the longer file are ignored.
XBy default,
X.I spiff
Xuses a Miller/Myers algorithm to find a minimal edit sequence
Xthat will convert the contents of the first file into the second.
X.TP
X\-<decimal-value>
Xsets a limit on the total number of insertions and deletions
Xthat will be considered.
XIf the files differ by more than the stated amount,
Xthe program will give up, print a warning message, and exit.
X.PP
XThe following options control the command script.  More than one of
Xeach may appear at at time. The commands accumulate.
X.TP
X.B \-f sfile
Xa command script to be taken from file
X.IR sfile 
X.TP
X.B \-s command-script
Xcauses the following argument to be taken as a command script.
X.PP
XThe following options control how individual objects are compared.
X.TP
X.B \-b
Xtreat all objects (including floating point numbers) as literals.
X.TP
X.B \-c
Xignore differences between upper and lower case.
X.PP
XThe following commands will control how the files are parsed.
X.TP
X.B \-w
Xtreat white space as objects.  Each white space character will
Xbe treated as a separate object when the program is comparing the
Xfiles.
X.TP
X.B \-m
Xtreat leading sign characters ( + and \- ) as separate even
Xif they are followed by floating point numbers.
X.TP
X.B \-d
Xtreat integer decimal numbers (such as 1987) as real numbers (subject to
Xtolerances) rather than as literal strings.
X.PP
XThe following three flags are used to set the default tolerances.
XThe floating-point-numbers may be given in the formats accepted
Xby atof(3).
X.TP
X.B \-a floating-point-number
Xspecifies an absolute value for the tolerance in floating point numbers.
XThe flag
X.B \-a1e-2
Xwill cause all differences greater than 0.01 to be reported.
X.TP
X.B \-r floating-point-number
Xspecifies a relative tolerance. The value given is interpreted
Xas a fraction of the larger (in absolute terms) 
Xof the two floating point numbers being compared.
XThus, the flag
X.B \-r0.1
Xwill cause the two floating point numbers 1.0 and 0.9 to be deemed within
Xtolerance. The numbers 1.0 and 0.89 will be outside the tolerance.
X.TP
X.B \-i
Xcauses differences between floating point numbers to be ignored.
X.PP
XIf more than one
X.B \-a, \-r,
Xor
X.B \-i
Xflag appear on the command line,
Xthe tolerances will be OR'd together (i.e. any difference that is within
Xany of the tolerances will be ignored). 
X.PP
XIf no default tolerances is set on the command line,
Xthe program will use a default tolerance of
X.B '\-a 1e-10 \-r 1e-10'.
X.SH SCRIPT COMMANDS
X.PP
XA script consists of commands, one per line.
XEach command consists of a keyword possibly followed by arguments.
XArguments are separated by one or more tabs or spaces.
XThe commands are:
X.TP
Xliteral BEGIN-STRING [END-STRING [ESCAPE-STRING]]
XSpecifies the delimiters surrounding text that is to be treated as a single
Xliteral object. If only one argument is present, then only that string itself is treated
Xas a literal. If only two arguments are present, they are taking as the starting
Xand ending delimiters respectively.  If three arguments are present, they are treated
Xas the start delimiter, end delimiter, and a string that may be used to escape
Xan instance of the end delimiter.
X.TP
Xbeginchar BEGINNING-OF-LINE-CHARACTER
XSet the the beginning of line character for BEGIN-STRING's in comments.
XThe default is '^'.
X.TP
Xendchar END-OF-LINE-CHARACTER
XSet the end of line character for END-STRING's in comments.
XThe default is '$'.
X.TP
Xaddalpha NEW-ALPHA-CHARACTER
XAdd NEW-ALPHA-CHARACTER to the set of characters allowed in literal strings.
XBy default, 
X.I spiff
Xparses sequences of characters that begin with a letter and followed by
Xzero or more letters or numbers as a single literal token.  This definition
Xis overly restrictive when dealing with programming languages.
XFor example, in the C programming language,
Xthe underscore character is allowed in identifiers. 
X.TP
Xcomment BEGIN-STRING [END-STRING [ESCAPE-STRING]]
XSpecifies the delimiters surrounding text that is to be be ignored entirely
X(i.e. viewed as comments).
XThe operation of the comment command is very similar to the literal command.
XIn addition, if the END-STRING consists of only
Xthe end of line character, the end of line will delimit the end of the comment.
XAlso, if the BEGIN-STRING starts with the beginning of line character, only
Xlines that begin with the BEGIN-STRING will be ignored.
X.PP
XMore than one comment specification and more than one literal string specification
Xmay be specified at a time.
X.TP
Xnestcom BEGIN-STRING [END-STRING [ESCAPE-STRING]]
XSimilar to the comment command, but allows comments to be nested.
XNote, due to the design of the parser nested comments can not
Xhave a BEGIN-STRING that starts with the beginning of line character.  
X.TP
Xresetcomments
XClears the list of comment specifications.
X.TP
Xresetliterals
XClears the list of literal specifications.
X.TP
Xtol [aVALUE\(brrVALUE\(bri\(brd . . . [ ; aVALUE\(brrVALUE\(bri\(brd . . . ] . . . ]
Xset the tolerance for floating point comparisons.  
XThe arguments to the tol command are a set of tolerance specifications
Xseparated by semicolons.  If more than one a,r,d, or i appears within
Xa specification, then the tolerances are OR'd together (i.e. any difference
Xthat is within any tolerance will be ignored).
XThe semantics of a,r, and i are identical to the
X.B \-a, \-r,
Xand
X.B \-i
Xflags. The d means that the default tolerance (as specified by the invocation
Xoptions) should be used.
XIf more than one specification appears on the line, the first
Xspecification is applied to the first floating point number on each line,
Xthe second specification to the second floating point number one each line
Xof the input files, and so on.  If there are more floating point numbers
Xon a given line of input than tolerance specifications,
Xthe last specification is used repeatedly for all remaining floating point numbers
Xon that line.
X.TP
Xcommand STRING
Xlines in the input file that start with STRING will be interpreted as
Xcommand lines. If no "command" is given as part of a
X.B \-s
Xor
X.B \-f
Xthen it will be impossible to embed commands in the input files.
X.TP
Xrem
X.TP
X#
Xused to places human readable remarks into a commands script. Note that the
Xuse of the '#' character differs from other command languages (for instance
Xthe Bourne shell).
X.I Spiff
Xwill only recognize the '#' as beginning a comment when it is the first
Xnon-blank character on the command line.  A '#' character appearing elsewhere
Xwill be treated as part of the command.  Cautious users should use 'rem'.
XThose hopelessly addicted to '#' as a comment character can have command
Xscripts with a familiar format.
X.PP
XTolerances specified in the command scripts have precedence over the tolerance
Xspecified on the invocation command line. The tolerance specified in
X.I file1
Xhas precedence over the tolerance specified in
X.I file2.
X.PP
X.SH VISUAL MODE
XIf
X.I spiff
Xis invoked with the \-v option, it will enter an interactive mode rather
Xthan produce an edit sequence.  Three windows will be put on the screen.
XTwo windows will contain corresponding segments of the input files.
XObjects that appear in both segments will be examined for differences and
Xif any difference is found, the objects will be highlighted in reverse video
Xon the screen.  Objects that appear in only one window will have a line drawn
Xthrough them to indicate that they aren't being compared with anything in the other
Xtext window. The third window is a command window.  The command window will
Xaccept a single tolerance specification (followed by a newline)
Xin a form suitable to the
X.B tol
Xcommand.  The tolerance specified will then be used as the default tolerance
Xand the display will be updated to highlight only those objects that exceed
Xthe new default tolerance.  Typing 
X.B m
X(followed by a newline) will display the next screenfull of text. Typing
X.B q
X(followed by a newline)  will cause the program to exit.
X.SH LIMITS
XEach input files can be no longer that 10,000 line long or contain more
Xthan 50,000 tokens. Longer files will be truncated.
XNo line can be longer than 1024 characters.  Newlines
Xwill be inserted every 1024 character.
X.SH EXAMPLES
X.TP
Xspiff \-e \-d foo bar
Xthis invocation (using exact match algorithm and treating integer numbers
Xas if they were floats) is very useful for examining large tables of numbers.
X.TP
Xspiff \-0 foo bar
Xcompare the two files, quitting after the first difference is found.
XThis makes the program operate roughly like cmp(1).
X.TP
Xspiff \-0 -q foo bar
Xsame as the above, but no output is produced.
XThe return code is still useful.
X.TP
Xspiff \-w \-b foo bar
Xwill make the program operate much like diff(1).
X.TP
Xspiff \-a1e-5 \-r0.001 foo bar
Xcompare the contents of the files foo and bar and ignore all differences between
Xfloating point numbers that are less than or equal to
X0.00001 or 0.1% of the number of larger magnitude.
X.TP
Xtol a.01 r.01
Xwill cause all differences between floating point numbers that are less than
Xor equal to
X0.01 or 1% of the number of larger magnitude to be ignored.
X.TP
Xtol a.01 r.01 ; i
Xwill cause the tolerance in the previous example to be applied to the first
Xfloating point number on each line.  All differences between the second and
Xsubsequent floating point numbers on each line will be ignored.
X.TP
Xtol a.01 r.01 ; i ; a.0001
Xlike the above except that only differences between the second floating point
Xnumber on each line will be ignored. The differences between
Xthird and subsequent floating point numbers on each number will be ignored if they
Xare less than or equal to 0.0001.
X.IP
XA useful script for examing C code is:
X.nf
Xliteral  "    "    \\ 
Xcomment  /*  */
Xliteral  &&
Xliteral  \(br\(br
Xliteral  <=
Xliteral  >=
Xliteral  !=
Xliteral  ==
Xliteral  --
Xliteral  ++
Xliteral  <<
Xliteral  >>
Xliteral  ->
Xaddalpha _
Xtol      a0
X.fi
X.IP
XA useful script for shell programs is:
X.nf
Xliteral  '    '    \\
Xcomment  #    $
Xtol      a0
X.fi
X.IP
XA useful script for Fortran programs is:
X.nf
Xliteral ' ' '
Xcomment ^C $
Xtol     a0
X.fi
X.IP
XA useful script for Modula 2 programs is:
X.nf
Xliteral ' '
Xliteral " "
Xnestcom (* *)
Xliteral :=
Xliteral <>
Xliteral <=
Xliteral >=
Xtol     a0
X.fi
X.IP
XA useful script for Lisp programs is:
X.nf
Xliteral " "
Xcomment ; $
Xtol     a0
X.fi
X.SH DIAGNOSTICS
X.I Spiff's
Xexit status is 0 if no differences are found, 1 if differences are found, and
X2 upon error.
X.SH BUGS
XIn C code, escaped newlines will appear as differences.
X.PP
XComments are treated as token delimiters.
X.PP
XComments in Basic don't work right.  The line number is not ignored.
X.PP
XContinuation lines in Fortran comments don't work.
X.PP
XThere is no way to represent strings specified using a
XHollerith notation in Fortran.
X.PP
XIn formated English text, hyphenated words,
Xmovements in pictures, footnotes, etc.
Xwill be reported as differences.
X.PP
XSTRING's in script commands can not include whitespace.
X.PP
XVisual mode does not handle tabs properly.  Files containing
Xtabs should be run through
Xexpand(1) before trying to display them with visual mode.
X.PP
XIn visual mode, the text windows appear in a fixed size and font.
XLines longer than the window size will not be handled properly.
X.PP
XObjects (literal strings) that contain newlines cause trouble in several places
Xin visual mode.
X.PP
XVisual mode should accept more than one tolerance specification.
X.PP
XWhen using visual mode or the exact match comparison algorithm, the program
Xshould do the parsing on the fly rather than truncating long files.
X.SH AUTHOR
XDaniel Nachbar
X.SH COPYRIGHT
X.nf
X                 Copyright (c) 1988 Bellcore
X                     All Rights Reserved
XPermission is granted to copy or use this program,
XEXCEPT that it may not be sold for profit, the copyright
Xnotice must be reproduced on copies, and credit should
Xbe given to Bellcore where it is due.
X         BELLCORE MAKES NO WARRANTY AND ACCEPTS
X            NO LIABILITY FOR THIS PROGRAM.
X.fi
X
X.br
X.SH SEE ALSO
Xatof(3)
Xisatty(2)
Xdiff(1)
Xcmp(1)
Xexpand(1)
Xmgr(1L)
X.PP
X"Spiff -- A Program for Making Controlled Approximate Comparisons of Files",
Xby Daniel Nachbar.
X.PP
X"A File Comparison Program" by Webb Miller and Eugene W. Myers in Software \-
XPractice and Experience, Volume 15(11), pp.1025-1040, (November 1985).
END_OF_FILE
if test 14738 -ne `wc -c <'spiff.1'`; then
    echo shar: \"'spiff.1'\" unpacked with wrong size!
fi
# end of 'spiff.1'
fi
echo shar: End of archive 3 \(of 4\).
cp /dev/null ark3isdone
MISSING=""
for I in 1 2 3 4 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 4 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.