Add the Spiff fp-aware diff utility from Bellcore

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@12875 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Brian Gaeke 2004-04-12 22:53:24 +00:00
parent 5f0b51547c
commit 0df5f9fb7e
38 changed files with 6986 additions and 0 deletions

18
utils/Spiff/LICENSE.TXT Normal file
View File

@ -0,0 +1,18 @@
The copyright below applies to Spiff 1.0 as redistributed as part of LLVM
and/or its test suite.
------------------------------------------------------------------------------
COPYRIGHT
Our lawyers advise the following:
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.
Given that all of the above seems to be very reasonable, there should be no
reason for anyone to not play by the rules.

6
utils/Spiff/Makefile Normal file
View File

@ -0,0 +1,6 @@
LEVEL = ../..
TOOLNAME = spiff
include $(LEVEL)/Makefile.common
CPPFLAGS += -DATT -DNOCHATTER

264
utils/Spiff/README Normal file
View File

@ -0,0 +1,264 @@
INSTALLATION
1) Change makefile settings to reflect
ATT vs. BSD software
termio vs. termcap
MGR vs. no MGR (MGR is a BELLCORE produced
window manager that is also available
free to the public.)
2) Then, just say "make".
If you want to "make install", you should first
change definition of INSDIR in the makefile
3) to test the software say
spiff Sample.1 Sample.2
spiff should find 4 differences and
you should see the words "added", "deleted", "changed",
and "altered" as well as four number in stand-out mode.
spiff Sample.1 Sample.2 | cat
should produce the same output, only the differences
should be underlined However, on many terminals the underlining
does not appear. So try the command
spiff Sample.1 Sample.2 | cat -v
or whatever the equivalent to cat -v is on your system.
A more complicated test set is found in Sample.3 and Sample.4
These files show how to use embedded commands to do things
like change the commenting convention and tolerances on the
fly. Be sure to run the command with the -s option to spiff:
spiff -s 'command spiffword' Sample.3 Sample.4
These files by no means provide an exhaustive test of
spiff's features. But they should give you some idea if things
are working right.
This code (or it's closely related cousins) has been run on
Vaxen running 4.3BSD, a CCI Power 6, some XENIX machines, and some
other machines running System V derivatives as well as
(thanks to eugene@ames.arpa) Cray, Amdahl and Convex machines.
4) Share and enjoy.
AUTHOR'S ADDRESS
Please send complaints, comments, praise, bug reports, etc to
Dan Nachbar
Bell Communications Research (also known as BELLCORE)
445 South St. Room 2B-389
Morristown, NJ 07960
nachbar@bellcore.com
or
bellcore!nachbar
or
(201) 829-4392 (praise only, please)
OVERVIEW OF OPERATION
Each of two input files is read and stored in core.
Then it is parsed into a series of tokens (literal strings and
floating point numbers, white space is ignored).
The token sequences are stored in core as well.
After both files have been parsed, a differencing algorithm is applied to
the token sequences. The differencing algorithm
produces an edit script, which is then passed to an output routine.
SIZE LIMITS AND OTHER DEFAULTS
file implementing limit name default value
maximum number of lines lines.h _L_MAXLINES 10000
per file
maximum number of tokens token.h K_MAXTOKENS 50000
per file
maximum line length misc.h Z_LINELEN 1024
maximum word length misc.h Z_WORDLEN 20
(length of misc buffers for
things like literal
delimiters.
NOT length of tokens which
can be virtually any length)
default absolute tolerance tol.h _T_ADEF "1e-10"
default relative tolerance tol.h _T_RDEF "1e-10"
maximum number of commands command.h _C_CMDMAX 100
in effect at one time
maximum number of commenting comment.h W_COMMAX 20
conventions that can be
in effect at one time
(not including commenting
conventions that are
restricted to beginning
of line)
maximum number of commenting comment.h W_BOLMAX 20
conventions that are
restricted to beginning of
line that are in effect at
one time
maximum number of literal comment.h W_LITMAX 20
string conventions that
can be in effect at one time
maximum number of tolerances tol.h _T_TOLMAX 10
that can be in effect at one
time
DIFFERENCES BETWEEN THE CURRENT VERSION AND THE ENCLOSED PAPER
The files paper.ms and paper.out contain the nroff -ms input and
output respectively of a paper on spiff that was given the Summer '88
USENIX conference in San Francisco. Since that time many changes
have been made to the code. Many flags have changed and some have
had their meanings reversed, see the enclosed man page for the current
usage. Also, there is no longer control over the
granularity of object used when applying the differencing algorithm.
The current version of spiff always applies the differencing
in terms of individual tokens. The -t flag controls how the edit script
is printed. This arrangement more closely reflects the original intent
of having multiple differencing granularities.
PERFORMANCE
Spiff is big and slow. It is big because all the storage is
in core. It is a straightforward but boring task to move the temporary
storage into a file. Someone who cares is invited to take on the job.
Spiff is slow because whenever a choice had to be made between
speed of operation and ease of coding, speed of operation almost always lost.
As the program matures it will almost certainly get smaller and faster.
Obvious performance enhancements have been avoided in order to make the
program available as soon as possible.
COPYRIGHT
Our lawyers advise the following:
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.
Given that all of the above seems to be very reasonable, there should be no
reason for anyone to not play by the rules.
NAMING CONVENTIONS USED IN THE CODE
All symbols (functions, data declarations, macros) are named as follows:
L_foo -- for names exported to other modules
and possibly used inside the module as well.
_L_foo -- for names used by more than one routine
within a module
foo -- for names used inside a single routine.
Each module uses a different value for "L" --
module files letter used implements
spiff.c Y top level routines
misc.[ch] Z various routines used throughout
strings.[ch] S routines for handling strings
edit.h E list of changes found and printed
tol.[ch] T tolerances for real numbers
token.[ch] K storage for objects
float.[ch] F manipulation of floats
floatrep.[ch] R representation of floats
line.[ch] L storage for input lines
parse.[ch] P parse for input files
command.[ch] C storage and recognition of commands
comment.[ch] W comment list maintenance
compare.[ch] X comparisons of a single token
exact.[ch] Q exact match differencing algorithm
miller.[ch] G miller/myers differencing algorithm
output.[ch] O print listing of differences
flagdefs.h U define flag bits that are used in
several of the other modules.
These #defines could have been
included in misc.c, but were separated
out because of their explicit
communication function.
visual.[ch] V screen oriented display for MGR
window manager, also contains
dummy routines for people who don't
have MGR
I haven't cleaned up visual.c yet. It probably doesn't even compile
in this version anyway. But since most people don't have mgr, this
isn't urgent.
NON-OBVIOUS DATA STRUCTURES
The Floating Point Representation
Floating point numbers are stored in a struct R_flstr
The fractional part is often called the mantissa.
The structure consists of
a flag for the sign of the factional part
the exponent in binary
a character string containing the fractional part
The structure could be converted to a float via
atof(strcat(".",mantissa)) * (10^exponent)
To be properly formed, the mantissa string must:
start with a digit between 1 and 9 (i.e. no leading zeros)
except for the zero, in which case the mantissa is exactly "0"
for the special case of zero, the exponent is always 0, and the
sign is always positive. (i.e. no negative 0)
In other words, (except for the value 0)
the mantissa is a fractional number ranging
between 0.1 (inclusive) and 1.0 (exclusive).
The exponent is interpreted as a power of 10.
Lines
there are three sets of lines:
implemented in line.c and line.h
real_lines --
the lines as they come from the file
content_lines --
a subset of reallines that excluding embedded commands
implemented in token.c and token.h
token_lines --
a subset of content_lines consisting of those lines that
have tokens that begin on them (literals can go on for
more than one line)
i.e. content_lines excluding comments and blank lines.
THE STATE OF THE CODE
Things that should be added
visual mode should handle tabs and wrapped lines
handling huge files in chunks when in using the ordinal match
algorithm. right now you have to parse and then diff the
whole thing before you get any output. often, you run out of memory.
Things that would be nice to add
output should optionally be expressed in real line numbers
(i.e. including command lines)
at present, all storage is in core. there should
be a compile time decision to allow temporary storage
in files rather than core.
that way the user could decide how to handle the
speed/space tradeoff
a front end that looked like diff should be added so that
one could drop spiff into existing shell scripts
the parser converts floats into their internal form even when
it isn't necessary.
in the miller/myer code, the code should check for matching
end sequences. it currently looks matching beginning
sequences.
Minor programming improvements (programming botches)
some of the #defines should really be enumerated types
all the routines in strings.c that alter the data at the end of
a pointer but return void should just return the correct
data. the current arrangement is a historical artifact
of the days when these routines returned a status code.
but then the code was never examined,
so i made them void . . .
comments should be added to the miller/myer code
in visual mode, ask for font by name rather than number

193
utils/Spiff/command.c Normal file
View File

@ -0,0 +1,193 @@
/* 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$";
#endif
#include "misc.h"
#include "tol.h"
#include "comment.h"
#include "command.h"
#include "strings.h"
#include "parse.h"
/*
** storage for the string that signals an embedded command
*/
static char _C_cmdword[Z_WORDLEN];
/*
** storage for the command script
*/
static int _C_nextcmd = 0;
static char *_C_cmds[_C_CMDMAX];
/*
** add a string to the command buffer
*/
void
C_addcmd(str)
char *str;
{
S_savestr(&_C_cmds[_C_nextcmd++],str);
return;
}
/*
** execute a single command
*/
static void
_C_do_a_cmd(str)
char *str;
{
/*
** place holder for the beginning of the string
*/
char *beginning = str;
S_skipspace(&str);
/*
** set the command string to allow embedded commands
*/
if (!S_wordcmp(str,"command"))
{
S_nextword(&str);
if (strlen(str) >= Z_WORDLEN)
{
Z_fatal("command word is too long");
}
S_wordcpy(_C_cmdword,str);
}
/*
** set the tolerances
*/
else if (!S_wordcmp(str,"tol"))
{
S_nextword(&str);
T_tolline(str);
}
/*
** add a comment specification
*/
else if (!S_wordcmp(str,"comment"))
{
S_nextword(&str);
if (strlen(str) >= Z_WORDLEN)
{
Z_fatal("command word is too long");
}
W_addcom(str,0);
}
else if (!S_wordcmp(str,"nestcom"))
{
S_nextword(&str);
if (strlen(str) >= Z_WORDLEN)
{
Z_fatal("command word is too long");
}
W_addcom(str,1);
}
/*
** add a literal string specification
*/
else if (!S_wordcmp(str,"literal"))
{
S_nextword(&str);
if (strlen(str) >= Z_WORDLEN)
{
Z_fatal("command word is too long");
}
W_addlit(str);
}
else if (!S_wordcmp(str,"resetcomments"))
{
W_clearcoms();
}
else if (!S_wordcmp(str,"resetliterals"))
{
W_clearlits();
}
else if (!S_wordcmp(str,"beginchar"))
{
S_nextword(&str);
W_setbolchar(*str);
}
else if (!S_wordcmp(str,"endchar"))
{
S_nextword(&str);
W_seteolchar(*str);
}
else if (!S_wordcmp(str,"addalpha"))
{
S_nextword(&str);
P_addalpha(str);
}
else if ((0 == strlen(str)) || !S_wordcmp(str,"rem")
|| ('#' == *str))
{
/* do nothing */
}
else
{
(void) sprintf(Z_err_buf,
"don't understand command %s\n",
beginning);
Z_fatal(Z_err_buf);
}
return;
}
/*
** execute the commands in the command buffer
*/
void
C_docmds()
{
int i;
for (i=0;i<_C_nextcmd;i++)
{
_C_do_a_cmd(_C_cmds[i]);
}
return;
}
/*
** disable embedded command key word recognition
*/
void
C_clear_cmd()
{
_C_cmdword[0] = '\0';
return;
}
#define inline spiff_inline
int
C_is_cmd(inline)
char *inline;
{
char *ptr;
/*
** see if this is a command line
** and if so, do the command right away
*/
if (('\0' != _C_cmdword[0]) && (!S_wordcmp(inline,_C_cmdword)))
{
ptr = inline;
S_nextword(&ptr);
_C_do_a_cmd(ptr);
return(1);
}
return(0);
}

18
utils/Spiff/command.h Normal file
View File

@ -0,0 +1,18 @@
/* 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 C_INCLUDED
extern int C_is_cmd();
extern void C_clear_cmd();
extern void C_addcmd();
extern void C_docmds();
#define _C_CMDMAX 100
#define C_INCLUDED
#endif

307
utils/Spiff/comment.c Normal file
View File

@ -0,0 +1,307 @@
/* 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$";
#endif
#include "misc.h"
#include "comment.h"
#include "strings.h"
/*
** storage for the comment specifiers that can appear
** anywhere on a line
*/
static int _W_nextcom = 0;
_W_comstruct _W_coms[_W_COMMAX];
/*
** storage for comment specifiers that are examined only at the
** beginning of each line
*/
static int _W_nextbol = 0;
_W_bolstruct _W_bols[_W_BOLMAX];
/*
** storage for delimiters of literal strings
*/
static int _W_nextlit = 0;
_W_litstruct _W_lits[_W_LITMAX];
/*
** storage for characters to specify beginning and end of line
** in the comment and literal commands
*/
char _W_bolchar = '^';
char _W_eolchar = '$';
/*
** build up a list of comment delimiters
*/
void
W_addcom(str,nestflag)
char *str;
int nestflag;
{
/*
** check for comments that begin at the beginning of line
*/
if (*str == _W_bolchar)
{
if (_W_nextbol >= _W_BOLMAX)
Z_fatal("too many beginning of line comment delimiter sets");
str++; /*skip the bol char */
S_wordcpy(_W_bols[_W_nextbol].begin,str);
S_nextword(&str);
if (*str == _W_eolchar)
{
(void) strcpy(_W_bols[_W_nextbol].end,"\n");
}
else
{
S_wordcpy(_W_bols[_W_nextbol].end,str);
}
S_nextword(&str);
S_wordcpy(_W_bols[_W_nextbol].escape,str);
/*
**
*/
if (nestflag)
Z_complain("begining of line comment won't nest");
_W_nextbol++;
}
else
{
if (_W_nextcom >= _W_COMMAX)
Z_fatal("too many comment delimiter sets");
S_wordcpy(_W_coms[_W_nextcom].begin,str);
S_nextword(&str);
if (*str == _W_eolchar)
{
(void) strcpy(_W_coms[_W_nextbol].end,"\n");
}
else
{
S_wordcpy(_W_coms[_W_nextbol].end,str);
}
S_nextword(&str);
S_wordcpy(_W_coms[_W_nextcom].escape,str);
_W_coms[_W_nextcom].nestbit = nestflag;
_W_nextcom++;
}
return;
}
/*
** clear the comment delimiter storage
*/
void
W_clearcoms()
{
_W_nextcom = 0;
_W_nextbol = 0;
return;
}
/*
** build up the list of literal delimiters
*/
void
W_addlit(str)
char *str;
{
if (_W_nextlit >= _W_LITMAX)
Z_fatal("too many literal delimiter sets");
S_wordcpy(_W_lits[_W_nextlit].begin,str);
S_nextword(&str);
S_wordcpy(_W_lits[_W_nextlit].end,str);
S_nextword(&str);
S_wordcpy(_W_lits[_W_nextlit].escape,str);
_W_nextlit++;
return;
}
/*
** clear the literal delimiter storage
*/
void
W_clearlits()
{
_W_nextlit = 0;
return;
}
static _W_bolstruct bol_scratch;
static void
_W_copybol(to,from)
W_bol to,from;
{
(void) strcpy(to->begin,from->begin);
(void) strcpy(to->end,from->end);
(void) strcpy(to->escape,from->escape);
}
W_bol
W_isbol(str)
char *str;
{
int i;
for(i=0;i<_W_nextbol;i++)
{
if(!S_wordcmp(str,_W_bols[i].begin))
{
_W_copybol(&bol_scratch,&_W_bols[i]);
return(&bol_scratch);
}
}
return(W_BOLNULL);
}
W_is_bol(ptr)
W_bol ptr;
{
int i;
for(i=0;i<_W_nextbol;i++)
{
if(!S_wordcmp(ptr->begin,_W_bols[i].begin) &&
!S_wordcmp(ptr->end,_W_bols[i].end) &&
!S_wordcmp(ptr->escape,_W_bols[i].escape))
{
return(1);
}
}
return(0);
}
static _W_litstruct lit_scratch;
static void
_W_copylit(to,from)
W_lit to,from;
{
(void) strcpy(to->begin,from->begin);
(void) strcpy(to->end,from->end);
(void) strcpy(to->escape,from->escape);
}
W_lit
W_islit(str)
char *str;
{
int i;
for(i=0;i<_W_nextlit;i++)
{
if(!S_wordcmp(str,_W_lits[i].begin))
{
_W_copylit(&lit_scratch,&_W_lits[i]);
return(&lit_scratch);
}
}
return(W_LITNULL);
}
W_is_lit(ptr)
W_lit ptr;
{
int i;
for(i=0;i<_W_nextlit;i++)
{
if(!S_wordcmp(ptr->begin,_W_lits[i].begin) &&
!S_wordcmp(ptr->end,_W_lits[i].end) &&
!S_wordcmp(ptr->escape,_W_lits[i].escape))
{
return(1);
}
}
return(0);
}
static _W_comstruct com_scratch;
static void
_W_copycom(to,from)
W_com to,from;
{
(void) strcpy(to->begin,from->begin);
(void) strcpy(to->end,from->end);
(void) strcpy(to->escape,from->escape);
to->nestbit = from->nestbit;
}
W_com
W_iscom(str)
char *str;
{
int i;
for(i=0;i<_W_nextcom;i++)
{
if(!S_wordcmp(str,_W_coms[i].begin))
{
_W_copycom(&com_scratch,&_W_coms[i]);
return(&com_scratch);
}
}
return(W_COMNULL);
}
W_is_com(ptr)
W_com ptr;
{
int i;
for(i=0;i<_W_nextcom;i++)
{
if(!S_wordcmp(ptr->begin,_W_coms[i].begin) &&
!S_wordcmp(ptr->end,_W_coms[i].end) &&
!S_wordcmp(ptr->escape,_W_coms[i].escape) &&
ptr->nestbit == _W_coms[i].nestbit)
{
return(1);
}
}
return(0);
}
W_is_nesting(ptr)
W_com ptr;
{
return(ptr->nestbit);
}

84
utils/Spiff/comment.h Normal file
View File

@ -0,0 +1,84 @@
/* 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 W_INCLUDED
#include <stdio.h>
#define _W_COMWORD 16
#define _W_COMMAX 20
#define _W_BOLMAX 20
#define _W_LITMAX 20
/*
** these three data structures used to be much
** different. eventually, the differences
** have disappeared as the code has evolved.
** obviously, they should now be collapsed.
** someday . . .
*/
typedef struct {
char begin[_W_COMWORD];
char end[_W_COMWORD];
char escape[_W_COMWORD];
} _W_bolstruct, *W_bol;
typedef struct {
char begin[_W_COMWORD];
char end[_W_COMWORD];
char escape[_W_COMWORD];
int nestbit;
} _W_comstruct, *W_com;
typedef struct {
char begin[_W_COMWORD];
char end[_W_COMWORD];
char escape[_W_COMWORD];
} _W_litstruct, *W_lit;
#define W_bolbegin(ptr) (ptr->begin)
#define W_bolend(ptr) (ptr->end)
#define W_bolescape(ptr) (ptr->escape)
#define W_litbegin(ptr) (ptr->begin)
#define W_litend(ptr) (ptr->end)
#define W_litescape(ptr) (ptr->escape)
#define W_combegin(ptr) (ptr->begin)
#define W_comend(ptr) (ptr->end)
#define W_comescape(ptr) (ptr->escape)
extern char _W_bolchar;
extern char _W_eolchar;
#define W_setbolchar(x) (_W_bolchar = x)
#define W_seteolchar(x) (_W_eolchar = x)
extern W_bol W_isbol();
extern W_lit W_islit();
extern W_com W_iscom();
extern int W_is_bol();
extern int W_is_lit();
extern int W_is_com();
extern _W_bolstruct _W_bols[];
extern _W_litstruct _W_lits[];
extern _W_comstruct _W_coms[];
extern void W_clearcoms();
extern void W_clearlits();
extern void W_addcom();
extern void W_addlit();
#define W_BOLNULL ((W_bol)0)
#define W_COMNULL ((W_com)0)
#define W_LITNULL ((W_lit)0)
#define W_INCLUDED
#endif

209
utils/Spiff/compare.c Normal file
View File

@ -0,0 +1,209 @@
/* 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$";
#endif
#include "misc.h"
#include "flagdefs.h"
#include "tol.h"
#include "token.h"
#include "line.h"
#include "float.h"
#include "compare.h"
#include <ctype.h>
X_com(a,b,flags)
int a,b,flags;
{
K_token atmp,btmp;
atmp = K_gettoken(0,a);
btmp = K_gettoken(1,b);
if(flags & U_BYTE_COMPARE)
{
return(_X_strcmp(K_gettext(atmp),K_gettext(btmp),flags));
}
else
{
return(_X_cmptokens(atmp,btmp,flags));
}
#ifndef lint
Z_fatal("this line should never be reached in com");
return(-1); /* Z_fatal never returns, but i need a this line
here to stop lint from complaining */
#endif
}
/*
** same as strcmp() except that case can be optionally ignored
*/
static int
_X_strcmp(s1,s2,flags)
char *s1,*s2;
int flags;
{
if (flags & U_NO_CASE)
{
for (;('\0' != s1) && ('\0' != *s2);s1++,s2++)
{
if(isalpha(*s1) && isalpha(*s2))
{
if(tolower(*s1) != tolower(*s2))
{
return(1);
}
}
else
{
if(*s1!=*s2)
{
return(1);
}
}
}
return(*s1 != *s2);
}
else
{
return(strcmp(s1,s2));
}
}
/*
** routine to compare two tokens
*/
static int
_X_cmptokens(p1,p2,flags)
K_token p1, p2;
int flags;
{
if (K_gettype(p1) != K_gettype(p2))
{
return(1);
}
switch (K_gettype(p1))
{
case K_LIT:
return(_X_strcmp(K_gettext(p1),K_gettext(p2),flags));
case K_FLO_NUM:
return(_X_floatdiff(K_getfloat(p1),
K_getfloat(p2),
T_picktol(K_gettol(p1),
K_gettol(p2))));
default:
Z_fatal("fell off switch in _X_cmptokens");
return(-1); /* Z_fatal never returns, but i need a this line
here to stop lint from complaining */
}
}
/*
** compare two F_floats using a tolerance
*/
static int
_X_floatdiff(p1,p2,the_tol)
F_float p1,p2;
T_tol the_tol;
{
F_float diff, float_tmp;
T_tol tol_tmp;
/*
** check for null tolerance list
*/
if (T_isnull(the_tol))
{
Z_fatal("_X_floatdiff called with a null tolerance");
}
/*
** look for an easy answer. i.e -- check
** to see if any of the tolerances are of type T_IGNORE
** or if the numbers are too small to exceed an absolute
** tolerance.
** if so, return immediately
*/
for(tol_tmp=the_tol; !(T_isnull(tol_tmp)) ;tol_tmp=T_getnext(tol_tmp))
{
if ((T_IGNORE == T_gettype(tol_tmp)) ||
/*
** take a look at the exponents before you bother
** with the mantissas
*/
((T_ABSOLUTE == T_gettype(tol_tmp))
&& !F_zerofloat(T_getfloat(tol_tmp))
&& (F_getexp(p1) <
F_getexp(T_getfloat(tol_tmp))-1)
&& (F_getexp(p2) <
F_getexp(T_getfloat(tol_tmp))-1)))
{
return(0);
}
}
/*
** ok, we're going to have to do some arithmetic, so
** first find the magnitude of the difference
*/
if (F_getsign(p1) != F_getsign(p2))
{
diff = F_floatmagadd(p1,p2);
}
else
{
diff = F_floatsub(p1,p2);
}
/*
** now check to see if the difference exceeds any tolerance
*/
for(tol_tmp=the_tol; !(T_isnull(tol_tmp)) ;tol_tmp=T_getnext(tol_tmp))
{
float_tmp = T_getfloat(tol_tmp);
if (T_gettype(tol_tmp) == T_ABSOLUTE)
{
/* do nothing */
}
else if (T_gettype(tol_tmp) == T_RELATIVE)
{
if (F_floatcmp(p1,p2) > 0)
{
float_tmp = F_floatmul(p1, float_tmp);
}
else
{
float_tmp = F_floatmul(p2, float_tmp);
}
}
else
{
Z_fatal("bad value for type of tolerance in floatdiff");
}
/*
** if we pass this tolerance, then we're done
*/
if (F_floatcmp(diff,float_tmp) <= 0)
{
return(0);
}
}
/*
** all of the tolerances were exceeded
*/
return(1);
}

14
utils/Spiff/compare.h Normal file
View File

@ -0,0 +1,14 @@
/* 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 X_INCLUDED
extern int X_com();
#define X_INCLUDED
#endif

43
utils/Spiff/edit.h Normal file
View File

@ -0,0 +1,43 @@
/* 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.
*/
/*
** the naming information hiding conventions are incompletely implemented
** for the edit module. I tried to clean it up once, but kept introducing
** nasty (ie. core dump) bugs in the miller code. I give up for now.
*/
#ifndef E_INCLUDED
#define E_INSERT 1
#define E_DELETE 2
typedef struct edt {
struct edt *link;
int op;
int line1;
int line2;
} _E_struct, *E_edit;
#define E_setop(x,y) ((x)->op = (y))
#define E_setl1(x,y) ((x)->line1 = (y))
#define E_setl2(x,y) ((x)->line2 = (y))
#define E_setnext(x,y) ((x)->link = (y))
#define E_getop(x) ((x)->op)
#define E_getl1(x) ((x)->line1)
#define E_getl2(x) ((x)->line2)
#define E_getnext(x) ((x)->link)
#define E_NULL ((E_edit) 0)
#define E_edit_alloc() (Z_ALLOC(1,_E_struct))
#define E_INCLUDED
#endif

92
utils/Spiff/exact.c Normal file
View File

@ -0,0 +1,92 @@
/* 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$";
#endif
#include "misc.h"
#include "edit.h"
/*
** routine to compare each object with its ordinal twin
*/
E_edit
Q_do_exact(size1,size2,max_d,comflags)
int size1;
int size2;
int max_d;
int comflags;
{
int i = 0;
int diffcnt = 0;
int last = Z_MIN(size1,size2);
int next_edit = 0;
E_edit last_ptr = E_NULL;
int start,tmp;
E_edit *script;
script = Z_ALLOC(max_d+1,E_edit);
if (size1 != size2)
{
(void) sprintf(Z_err_buf,"unequal number of tokens, %d and %d respectively\n",size1,size2);
Z_complain(Z_err_buf);
}
do
{
/*
** skip identical objects
*/
while (i<last && (!X_com(i,i,comflags)))
{
i++;
}
start = i;
/*
** see how many difference we have in a row
*/
while (i<last && X_com(i,i,comflags))
{
if ((diffcnt += 2) >= max_d+1)
Z_exceed(max_d);
i++;
}
/*
** build the list of deletions
*/
for(tmp=start;tmp<i;tmp++,next_edit++)
{
script[next_edit] = E_edit_alloc();
E_setnext(script[next_edit],last_ptr);
last_ptr = script[next_edit];
E_setop(script[next_edit],E_DELETE);
E_setl1(script[next_edit],tmp+1);
/* no need to set line2, it is never used */
E_setl2(script[next_edit],0);
}
/*
** build the list of insertions
*/
for(tmp=start;tmp<i;tmp++,next_edit++)
{
script[next_edit] = E_edit_alloc();
E_setnext(script[next_edit],last_ptr);
last_ptr = script[next_edit];
E_setop(script[next_edit],E_INSERT);
E_setl1(script[next_edit],i);
E_setl2(script[next_edit],tmp+1);
}
} while (i<last);
return(last_ptr);
}

17
utils/Spiff/exact.h Normal file
View File

@ -0,0 +1,17 @@
/* 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 Q_INCLUDED
#include "edit.h"
extern E_edit Q_do_exact();
#define Q_INCLUDED
#endif

29
utils/Spiff/flagdefs.h Normal file
View File

@ -0,0 +1,29 @@
/* 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.
*/
/*
** flags used by both parser and comparison routines
*/
#define U_INCLUDE_WS 001
/*
** flags used only by the comparison routines
*/
#define U_BYTE_COMPARE 002
#define U_NO_CASE 004
/*
** flag used by the output routine
*/
#define U_TOKENS 010
/*
** flags used only by the parser
*/
#define U_INC_SIGN 020
#define U_NEED_DECIMAL 040

811
utils/Spiff/float.c Normal file
View File

@ -0,0 +1,811 @@
/* 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$";
#endif
#include <ctype.h>
#include "misc.h"
#include "floatrep.h"
#include "float.h"
#include "strings.h"
#define _F_GETEND(x) (x + (strlen(x)-1))
/*
int floatcnt = 0;
*/
/*
** routines to convert strings to our internal floating point form
** isfloat just looks at the string
** to see if a conversion is reasonable
** it does look-ahead on when it sees an 'e' and such.
** atocf actually does the conversion.
** these two routines could probably be combined
*/
/*
** test to see if the string can reasonably
** be interpreted as floating point number
** returns 0 if string can't be interpreted as a float
** otherwise returns the number of digits that will be used in F_atof
*/
F_isfloat(str,need_decimal,allow_sign)
char *str;
int need_decimal; /* if non-zero, require that a decimal point be present
otherwise, accept strings like "123" */
int allow_sign; /* if non-zero, allow + or - to set the sign */
{
int man_length = 0; /* length of the fractional part (mantissa) */
int exp_length = 0; /* length of the exponential part */
int got_a_digit = 0; /* flag to set if we ever see a digit */
/*
** look for an optional leading sign marker
*/
if (allow_sign && ('+' == *str || '-' == *str))
{
str++; man_length++;
}
/*
** count up the digits on the left hand side
** of the decimal point
*/
while(isdigit(*str))
{
got_a_digit = 1;
str++; man_length++;
}
/*
** check for a decimal point
*/
if ('.' == *str)
{
str++; man_length++;
}
else
{
if (need_decimal)
{
return(0);
}
}
/*
** collect the digits on the right hand
** side of the decimal point
*/
while(isdigit(*str))
{
got_a_digit = 1;
str++; man_length++;
}
if (!got_a_digit)
return(0);
/*
** now look ahead for an exponent
*/
if ('e' == *str ||
'E' == *str ||
'd' == *str ||
'D' == *str)
{
str++; exp_length++;
if ('+' == *str || '-' == *str)
{
str++; exp_length++;
}
if (!isdigit(*str))
{
/*
** look ahead went too far,
** so return just the length of the mantissa
*/
return(man_length);
}
while (isdigit(*str))
{
str++; exp_length++;
}
}
return(man_length+exp_length); /* return the total length */
}
/*
** routine to convert a string to our internal
** floating point representation
**
** similar to atof()
*/
F_float
F_atof(str,allflag)
char *str;
int allflag; /* require that exactly all the characters are used */
{
char *beg = str; /* place holder for beginning of the string */
char man[R_MANMAX]; /* temporary location to build the mantissa */
int length = 0; /* length of the mantissa so far */
int got_a_digit = 0; /* flag to set if we get a non-zero digit */
int i;
int resexp;
F_float res; /* where we build the result */
/*
floatcnt++;
*/
res = R_makefloat();
R_setsign(res,R_POSITIVE);
resexp = 0;
man[0] = '\0';
/*
** check for leading sign
*/
if ('+' == *str)
{
/*
** sign should already be positive, see above in this
** routine, so just skip the plus sign
*/
str++;
}
else
{
if ('-' == *str)
{
R_setsign(res,R_NEGATIVE);
str++;
}
}
/*
** skip any leading zeros
*/
while('0' == *str)
{
str++;
}
/*
** now snarf up the digits on the left hand side
** of the decimal point
*/
while(isdigit(*str))
{
got_a_digit = 1;
man[length++] = *str++;
man[length] = '\0';
resexp++;
}
/*
** skip the decimal point if there is one
*/
if ('.' == *str)
str++;
/*
** trim off any leading zeros (on the right hand side)
** if there were no digits in front of the decimal point.
*/
if (!got_a_digit)
{
while('0' == *str)
{
str++;
resexp--;
}
}
/*
** now snarf up the digits on the right hand side
*/
while(isdigit(*str))
{
man[length++] = *str++;
man[length] = '\0';
}
if ('e' == *str ||
'E' == *str ||
'd' == *str ||
'D' == *str )
{
str++;
resexp += atoi(str);
}
if (allflag)
{
if ('+' == *str ||
'-' == *str)
{
str++;
}
while (isdigit(*str))
{
str++;
}
if ('\0' != *str)
{
(void) sprintf(Z_err_buf,
"didn't use up all of %s in atocf",
beg);
Z_fatal(Z_err_buf);
}
}
/*
** check for special case of all zeros in the mantissa
*/
for (i=0;i<length;i++)
{
if (man[i] != '0')
{
/*
** the mantissa is non-zero, so return it unchanged
*/
S_trimzeros(man);
/*
** save a copy of the mantissa
*/
R_setfrac(res,man);
R_setexp(res,resexp);
return(res);
}
}
/*
** the answer is 0, so . . .
*/
R_setzero(res);
return(res);
}
/*
** add s2 to s1
*/
static
void
_F_stradd(s1,s2)
char *s1,*s2;
{
char *end1 = s1 + (strlen(s1)-1);
char *end2 = s2 + (strlen(s2)-1);
static char result[R_MANMAX];
char *resptr = result+(R_MANMAX-1); /*point to the end of the array */
int carry = 0;
int tmp,val1,val2;
*resptr-- = '\0';
while ((end1 >= s1) || ( end2 >= s2))
{
if (end1 >= s1)
{
val1 = *end1 - '0';
--end1;
}
else
{
val1 = 0;
}
if (end2 >= s2)
{
val2 = *end2 - '0';
--end2;
}
else
{
val2 = 0;
}
tmp = val1 + val2 + carry;
if (tmp > 9)
{
carry = 1;
tmp -= 10;
}
else
{
carry = 0;
}
*resptr-- = tmp+'0';
}
if (carry)
{
*resptr = '1';
}
else
{
resptr++;
}
(void) strcpy(s1,resptr);
return;
}
/*
** add zero(s) onto the end of a string
*/
static void
addzeros(ptr,count)
char *ptr;
int count;
{
for(;count> 0;count--)
{
(void) strcat(ptr,"0");
}
return;
}
/*
** subtract two mantissa strings
*/
F_float
F_floatsub(p1,p2)
F_float p1,p2;
{
static F_float result;
static needinit = 1;
static char man1[R_MANMAX],man2[R_MANMAX],diff[R_MANMAX];
int exp1,exp2;
char *diffptr,*big,*small;
int man_cmp_val,i,borrow;
if (needinit)
{
result = R_makefloat();
needinit = 0;
}
man1[0] = '\0';
man2[0] = '\0';
exp1 = R_getexp(p1);
exp2 = R_getexp(p2);
/*
** line up the mantissas
*/
while (exp1 < exp2)
{
(void) strcat(man1,"0");
exp1++;
}
while(exp1 > exp2)
{
(void) strcat(man2,"0");
exp2++;
}
if (exp1 != exp2) /* boiler plate assertion */
{
Z_fatal("mantissas didn't get lined up properly in floatsub");
}
(void) strcat(man1,R_getfrac(p1));
(void) strcat(man2,R_getfrac(p2));
/*
** now that the mantissa are aligned,
** if the strings are the same, return 0
*/
if((man_cmp_val = strcmp(man1,man2)) == 0)
{
R_setzero(result);
return(result);
}
/*
** pad the shorter string with 0's
** when this loop finishes, both mantissas should
** have the same length
*/
if (strlen(man1)> strlen(man2))
{
addzeros(man2,strlen(man1)-strlen(man2));
}
else
{
if (strlen(man1)<strlen(man2))
{
addzeros(man1,strlen(man2)-strlen(man1));
}
}
if (strlen(man1) != strlen(man2)) /* pure boilerplate */
{
Z_fatal("lengths not equal in F_floatsub");
}
if (man_cmp_val < 0)
{
big = man2;
small = man1;
}
else
{
big = man1;
small = man2;
}
/*
** find the difference between the mantissas
*/
for(i=(strlen(big)-1),borrow=0,diff[strlen(big)] = '\0';i>=0;i--)
{
char from;
if (borrow)
{
if (big[i] == '0')
{
from = '9';
}
else
{
from = big[i]-1;
borrow = 0;
}
}
else
{
if(big[i]<small[i])
{
from = '9'+1;
borrow = 1;
}
else
{
from = big[i];
}
}
diff[i] = (from-small[i]) + '0';
}
/*
** trim the leading zeros on the difference
*/
diffptr = diff;
while('0' == *diffptr)
{
diffptr++;
exp1--;
}
R_setexp(result,exp1); /* exponents are equal at the point */
R_setfrac(result,diffptr);
R_setsign(result,R_POSITIVE);
return(result);
}
F_floatcmp(f1,f2)
F_float f1,f2;
{
static char man1[R_MANMAX],man2[R_MANMAX];
/*
** special case for zero
*/
if (R_zerofloat(f1))
{
if (R_zerofloat(f2))
{
return(0);
}
else
{
return(-1);
}
}
else
{
if (R_zerofloat(f2))
{
return(1);
}
}
/*
** to reach this point, both numbers must be non zeros
*/
if (R_getexp(f1) < R_getexp(f2))
{
return(-1);
}
if (R_getexp(f1) > R_getexp(f2))
{
return(1);
}
(void) strcpy(man1,R_getfrac(f1));
S_trimzeros(man1);
(void) strcpy(man2,R_getfrac(f2));
S_trimzeros(man2);
return(strcmp(man1,man2));
}
F_float
F_floatmul(f1,f2)
F_float f1,f2;
{
static char prod[R_MANMAX];
char *end;
int count1 = 0;
int count2 = 0;
int tmp,len;
char *end1;
char *end2;
static char man1[R_MANMAX],man2[R_MANMAX];
char *bigman,*smallman;
static F_float result;
static int needinit = 1;
if (needinit)
{
result = R_makefloat();
needinit = 0;
}
/*
** special case for a zero result
*/
if (R_zerofloat(f1) || R_zerofloat(f2))
{
R_setzero(result);
return(result);
}
(void) strcpy(man1,R_getfrac(f1));
(void) strcpy(man2,R_getfrac(f2));
end1 = _F_GETEND(man1);
end2 = _F_GETEND(man2);
/*
** decide which number will cause multiplication loop to go
** around the least
*/
while(end1 >= man1)
{
count1 += *end1 - '0';
end1--;
}
while(end2 >= man2)
{
count2 += *end2 - '0';
end2--;
}
if (count1 > count2)
{
bigman = man1;
smallman = man2;
}
else
{
bigman = man2;
smallman = man1;
}
S_trimzeros(bigman);
S_trimzeros(smallman);
len = strlen(bigman) + strlen(smallman);
end = _F_GETEND(smallman);
(void) strcpy(prod,"0");
/*
** multiplication by repeated addition
*/
while(end >= smallman)
{
for(tmp = 0;tmp<*end-'0';tmp++)
{
_F_stradd(prod,bigman);
}
addzeros(bigman,1);
end--;
}
R_setfrac(result,prod);
R_setexp(result,(((R_getexp(f1) + R_getexp(f2)) - len)+ strlen(prod)));
if (R_getsign(f1) == R_getsign(f2))
{
R_setsign(result,R_POSITIVE);
}
else
{
R_setsign(result,R_NEGATIVE);
}
return(result);
}
_F_xor(x,y)
{
return(((x) && !(y)) || (!(x) && (y)));
}
#define _F_SAMESIGN(x,y) _F_xor((x<0),(y<0))
#define _F_ABSADD(x,y) (Z_ABS(x) + Z_ABS(y))
_F_ABSDIFF(x,y)
{
if (Z_ABS(x) < Z_ABS(y))
{
return(Z_ABS(y) - Z_ABS(x));
}
else
{
return(Z_ABS(x) - Z_ABS(y));
}
}
/*
** add two floats without regard to sign
*/
F_float
F_floatmagadd(p1,p2)
F_float p1,p2;
{
static F_float result;
static int needinit = 1;
static char man1[R_MANMAX],man2[R_MANMAX];
int digits; /* count of the number of digits needed to represent the
result */
int resexp; /* exponent of the result */
int len; /* length of the elements before adding */
char *diffptr;
if (needinit)
{
result = R_makefloat();
needinit = 0;
}
(void) strcpy(man1,"");
(void) strcpy(man2,"");
/*
** find the difference in the exponents number of digits
*/
if( _F_SAMESIGN(R_getexp(p1),R_getexp(p2)))
{
digits = _F_ABSDIFF(R_getexp(p1),R_getexp(p2));
}
else
{
digits = _F_ABSADD(R_getexp(p1),R_getexp(p2));
}
/*
** make sure that there is room to store the result
*/
if (digits>0)
{
if (R_getexp(p1) < R_getexp(p2))
{
/*
** leave room for terminator
*/
if (digits+strlen(R_getfrac(p1)) > (R_MANMAX-1))
{
(void) sprintf(Z_err_buf,
"numbers differ by too much in magnitude");
Z_fatal(Z_err_buf);
}
}
else
{
/*
** leave room for terminator
*/
if (digits+strlen(R_getfrac(p2)) > (R_MANMAX-1))
{
(void) sprintf(Z_err_buf,
"numbers differ by too much in magnitude");
Z_fatal(Z_err_buf);
}
}
}
else
{
/*
** leave room for terminator and possible carry
*/
if (Z_MAX(strlen(R_getfrac(p1)),
strlen(R_getfrac(p2))) > (R_MANMAX-2))
{
(void) sprintf(Z_err_buf,
"numbers differ by too much in magnitude");
Z_fatal(Z_err_buf);
}
}
/*
** pad zeroes on the front of the smaller number
*/
if (R_getexp(p1) < R_getexp(p2))
{
addzeros(man1,digits);
resexp = R_getexp(p2);
}
else
{
addzeros(man2,digits);
resexp = R_getexp(p1);
}
(void) strcat(man1,R_getfrac(p1));
(void) strcat(man2,R_getfrac(p2));
len = Z_MAX(strlen(man1),strlen(man2));
/*
** add the two values
*/
_F_stradd(man1,man2);
/*
** adjust the exponent to account for a
** possible carry
*/
resexp += strlen(man1) - len;
/*
** trim the leading zeros on the sum
*/
diffptr = man1;
while('0' == *diffptr)
{
diffptr++;
resexp--;
}
R_setfrac(result,diffptr);
R_setexp(result,resexp);
R_setsign(result,R_POSITIVE);
return(result);
}
/*
** useful debugging routine. we don't call it in the release,
** so it is commented out, but we'll leave it for future use
*/
/*
F_printfloat(fl)
F_float fl;
{
(void) printf("fraction = :%s: exp = %d sign = %c\n",
R_getfrac(fl),
R_getexp(fl),
((R_getsign(fl) == R_POSITIVE) ? '+': '-'));
}
*/

35
utils/Spiff/float.h Normal file
View File

@ -0,0 +1,35 @@
/* 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.
*/
#include "floatrep.h"
#ifndef F_INCLUDED
/*
** flags for F_atof
*/
#define NO_USE_ALL 0
#define USE_ALL 1
typedef struct R_flstr *F_float;
#define F_getexp(x) R_getexp(x)
#define F_getsign(x) R_getsign(x)
#define F_zerofloat(x) R_zerofloat(x)
extern F_float F_atof();
extern F_float F_floatmul();
extern F_float F_floatmagadd();
extern F_float F_floatsub();
#define F_null ((F_float) 0)
#define F_INCLUDED
#endif

32
utils/Spiff/floatrep.c Normal file
View File

@ -0,0 +1,32 @@
/* 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$";
#endif
#include "misc.h"
#include "floatrep.h"
R_float
R_makefloat()
{
R_float retval;
retval = Z_ALLOC(1,struct R_flstr);
retval->mantissa = Z_ALLOC(R_MANMAX,char);
return(retval);
}
R_getexp(ptr)
R_float ptr;
{
return(ptr->exponent);
}

66
utils/Spiff/floatrep.h Normal file
View File

@ -0,0 +1,66 @@
/* 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.
*/
/*
** header file that defines canonical floating point structure
** and routines
*/
#ifndef R_INCLUDED
/*
** when evaluated to a string, the fractional part will
** not exceed this length
*/
#define R_MANMAX 200
#define R_POSITIVE 0
#define R_NEGATIVE 1
struct R_flstr {
int exponent;
int man_sign;
char *mantissa;
};
typedef struct R_flstr *R_float;
#define R_getfrac(x) (x->mantissa)
extern R_float R_makefloat();
extern int R_getexp();
#define R_getsign(x) (x->man_sign)
/*
** takes a string
*/
#define R_setfrac(x,y) ((void)strcpy(x->mantissa,y))
/*
** takes an int
*/
#define R_setexp(x,y) (x->exponent = y)
/*
** takes a sign
*/
#define R_setsign(x,y) (x->man_sign = y)
/*
#define R_incexp(x) ((x->exponent)++)
#define R_decexp(x) ((x->exponent)--)
*/
#define R_setzero(x) R_setfrac(x,"0");R_setexp(x,0);R_setsign(x,R_POSITIVE)
#define R_zerofloat(x) ((0 == x->exponent) && (!strcmp(x->mantissa,"0")))
#define R_INCLUDED
#endif

169
utils/Spiff/line.c Normal file
View File

@ -0,0 +1,169 @@
/* 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$";
#endif
#include <stdio.h>
#include "misc.h"
#include "token.h"
#include "line.h"
char *_L_al[_L_MAXLINES]; /* storage for lines */
char *_L_bl[_L_MAXLINES];
int _L_ai[_L_MAXLINES]; /* index from token line number to first token */
int _L_bi[_L_MAXLINES];
int _L_ac[_L_MAXLINES]; /* count of tokens on this token line */
int _L_bc[_L_MAXLINES];
int _L_arlm; /* count of real lines in the file */
int _L_brlm;
int _L_aclm; /* count of content lines in the file */
int _L_bclm;
int _L_atlm; /* count of token lines in the file */
int _L_btlm;
int _L_aclindex[_L_MAXLINES]; /* mapping from content lines to real lines*/
int _L_bclindex[_L_MAXLINES];
int _L_atlindex[_L_MAXLINES]; /*mapping from token lines to content lines */
int _L_btlindex[_L_MAXLINES];
static void
_L_setrline(file,X,str)
int file;
int X;
char *str;
{
if (file)
{
S_savestr(&_L_bl[X],str);
}
else
{
S_savestr(&_L_al[X],str);
}
return;
}
/*
** returns 1 if we reached the end of file
** returns 0 if there is more to do
**
** stores data and sets maximum counts
*/
L_init_file(fnumber,fname)
int fnumber;
char *fname;
{
extern char *fgets();
FILE *fp;
static char buf[Z_LINELEN+2]; /* +2 is to leave room for us to add
a newline if we need to */
int ret_val = 1;
int tmplen;
if ((fp = fopen(fname,"r")) == (FILE*) NULL)
{
(void) sprintf(Z_err_buf, "Cannot open file %s.\n",fname);
Z_fatal(Z_err_buf);
}
/*
** clear the line count
*/
_L_setrlmx(fnumber,0);
/*
** read in the entire file
*/
while (fgets(buf,Z_LINELEN+1,fp) != (char *) NULL)
{
tmplen = strlen(buf);
if (tmplen <= 0)
{
(void) sprintf(Z_err_buf,
"fatal error -- got 0 length line %d in file %s\n",
L_getrlmax(fnumber)+1,
fname);
Z_fatal(Z_err_buf);
}
else if (tmplen > Z_LINELEN)
{
(void) sprintf(Z_err_buf,
"got fatally long line %d in file %s length is %d, must be a bug\n",
L_getrlmax(fnumber)+1,
fname,tmplen);
Z_fatal(Z_err_buf);
}
/*
** look for newline as last character
*/
if ('\n' != buf[tmplen-1])
{
/*
** did we run out room in the buffer?
*/
if (tmplen == Z_LINELEN)
{
(void) sprintf(Z_err_buf,
"line %d too long in file %s, newline added after %d characters\n",
L_getrlmax(fnumber)+1,
fname,Z_LINELEN);
Z_complain(Z_err_buf);
}
else
{
(void) sprintf(Z_err_buf,
"didn't find a newline at end of line %d in file %s, added one\n",
L_getrlmax(fnumber)+1,
fname);
Z_complain(Z_err_buf);
}
buf[tmplen] = '\n';
buf[tmplen+1] = '\0';
}
_L_setrline(fnumber,L_getrlmax(fnumber),buf);
if (L_getrlmax(fnumber) >= _L_MAXLINES-1)
{
(void) sprintf(Z_err_buf,
"warning -- ran out of space reading %s, truncated to %d lines\n",
fname,_L_MAXLINES);
Z_complain(Z_err_buf);
ret_val= 0;
break;
}
else
{
/*
** increment the line count
*/
_L_incrlmx(fnumber);
}
}
(void) fclose(fp);
/*
** reset line numbers
*/
L_setclmax(fnumber,0);
L_settlmax(fnumber,0);
return(ret_val);
}

113
utils/Spiff/line.h Normal file
View File

@ -0,0 +1,113 @@
/* 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 L_INCLUDED
#define _L_MAXLINES 10000
/*
** oh god, is this an ugly implementation.
** I really should have a two dimensional array of structures
** the history of the current arrangement is too long
** and ugly to record here.
** Someday when I have too much time on my hands . . .
*/
extern char *_L_al[]; /* storage for text in first file */
extern char *_L_bl[]; /* storage for text in second file */
extern int _L_ai[]; /* pointer from token line to first token */
extern int _L_bi[];
extern int _L_ac[]; /* number of tokens on a given token line */
extern int _L_bc[];
extern int _L_aclindex[]; /* mapping from content lines to real lines */
extern int _L_bclindex[];
extern int _L_atlindex[]; /* mapping from lines with tokens to content lines */
extern int _L_btlindex[];
extern int _L_arlm; /* count of real lines */
extern int _L_brlm;
extern int _L_aclm; /* count of content lines */
extern int _L_bclm;
extern int _L_atlm; /* count of lines with tokens */
extern int _L_btlm;
/*
** routines to set up mappings from token lines to content lines
** and from content lines to real lines
*/
#define L_setclindex(file,content,real) (file?(_L_bclindex[content]=real):\
(_L_aclindex[content]=real))
#define L_settlindex(file,token,content) (file?(_L_btlindex[token]=content):\
(_L_atlindex[token]=content))
/*
** get line number X from file
*/
#define L_getrline(file, X) (file?(_L_bl[X]):(_L_al[X]))
#define L_getcline(file, X) (file?(_L_bl[_L_bclindex[X]]):\
(_L_al[_L_aclindex[X]]))
#define L_gettline(file, X) (file?(_L_bl[_L_bclindex[_L_btlindex[X]]]):\
(_L_al[_L_aclindex[_L_atlindex[X]]]))
#define L_cl2rl(file, X) (file?(_L_bclindex[X]):\
(_L_aclindex[X]))
#define L_tl2cl(file, X) (file?(_L_btlindex[X]):\
(_L_atlindex[X]))
#define L_tl2rl(file, X) (file?(_L_bclindex[_L_btlindex[X]]):\
(_L_aclindex[_L_atlindex[X]]))
/*
** get number of first token on line X of the file
*/
#define L_getindex(file,X) (file?(_L_bi[X]):(_L_ai[X]))
/*
** get count of number of tokens on line X of first file
*/
#define L_getcount(file,X) (file?(_L_bc[X]):(_L_ac[X]))
/*
** save number of first token for line X of file
*/
#define L_setindex(file,index,value) (file?(_L_bi[index]=value):(_L_ai[index]=value))
/*
** save count of tokens on line X of file
*/
#define L_setcount(file,index,value) (file?(_L_bc[index]=value):(_L_ac[index]=value))
#define L_inccount(file,index) (file?(_L_bc[index]++):(_L_ac[index]++))
/*
** retrieve line and token counts
*/
#define L_getrlmax(file) (file?_L_brlm:_L_arlm)
#define L_getclmax(file) (file?_L_bclm:_L_aclm)
#define L_gettlmax(file) (file?_L_btlm:_L_atlm)
/*
** set line and token counts
*/
#define _L_setrlmx(file,value) (file?(_L_brlm=(value)):(_L_arlm=(value)))
#define L_setclmax(file,value) (file?(_L_bclm=(value)):(_L_aclm=(value)))
#define L_settlmax(file,value) (file?(_L_btlm=(value)):(_L_atlm=(value)))
/*
** increment line and token counts
*/
#define _L_incrlmx(file) (file?(_L_brlm++):(_L_arlm++))
#define L_incclmax(file) (file?(_L_bclm++):(_L_aclm++))
#define L_inctlmax(file) (file?(_L_btlm++):(_L_atlm++))
#define L_INCLUDED
#endif

127
utils/Spiff/miller.c Normal file
View File

@ -0,0 +1,127 @@
/* 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$";
#endif
#include "misc.h"
#include "token.h"
#include "edit.h"
#define MAXT K_MAXTOKENS
#define ORIGIN (max_obj/2)
#define MILLER_CHATTER 100
/*
** totally opaque miller/myers code
** hacked from a version provided by the author
*/
E_edit
G_do_miller(m,n,max_d,comflags)
int m;
int n;
int max_d;
int comflags;
{
int max_obj = m + n;
int
lower,
upper,
d,
k,
row,
col;
E_edit new;
#ifdef STATIC_MEM
static E_edit script[MAXT+1];
static int last_d[MAXT+1];
#else
E_edit *script;
int *last_d;
/*
** make space for the two big arrays
** these could probably be smaller if I
** understood this algorithm at all
** as is, i just shoe horned it into my program.
** be sure to allocate max_obj + 1 objects as was done
** in original miller/myers code
*/
script = Z_ALLOC(max_obj+1,E_edit);
last_d = Z_ALLOC(max_obj+1,int);
#endif
for (row=0;row < m && row < n && X_com(row,row,comflags) == 0; ++row)
;
last_d[ORIGIN] = row;
script[ORIGIN] = E_NULL;
lower = (row == m) ? ORIGIN+1 : ORIGIN - 1;
upper = (row == n) ? ORIGIN-1 : ORIGIN + 1;
if (lower > upper)
{
/*
** the files are identical
*/
return(E_NULL);
}
for (d = 1; d <= max_d; ++d) {
for (k = lower; k<= upper; k+= 2) {
new = E_edit_alloc();
if (k == ORIGIN-d || k!= ORIGIN+d && last_d[k+1] >= last_d[k-1]) {
row = last_d[k+1]+1;
E_setnext(new,script[k+1]);
E_setop(new,E_DELETE);
} else {
row = last_d[k-1];
E_setnext(new,script[k-1]);
E_setop(new,E_INSERT);
}
E_setl1(new,row);
col = row + k - ORIGIN;
E_setl2(new,col);
script[k] = new;
while (row < m && col < n && X_com(row,col,comflags) == 0) {
++row;
++col;
}
last_d[k] = row;
if (row == m && col == n) {
return(script[k]);
}
if (row == m)
lower = k+2;
if (col == n)
upper = k-2;
}
--lower;
++upper;
#ifndef NOCHATTER
if ((d > 0) && (0 == (d % MILLER_CHATTER)))
{
(void) sprintf(Z_err_buf,
"found %d differences\n",
d);
Z_chatter(Z_err_buf);
}
#endif
}
Z_exceed(max_d);
/*
** dummy lines to shut up lint
*/
Z_fatal("fell off end of do_miller\n");
return(E_NULL);
}

17
utils/Spiff/miller.h Normal file
View File

@ -0,0 +1,17 @@
/* 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 G_INCLUDED
#include "edit.h"
extern E_edit G_do_miller();
#define G_INCLUDED
#endif

119
utils/Spiff/misc.c Normal file
View File

@ -0,0 +1,119 @@
/* 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$";
#endif
#include <stdio.h>
#include "misc.h"
#include "visual.h"
#include "output.h"
/*
** various routines used throughout the program
*/
static int _Z_qflag = 0;
void
Z_setquiet()
{
_Z_qflag = 1;
}
char Z_err_buf[Z_LINELEN];
#ifndef NOCHATTER
/*
** I/O coverup to reassure users with HUGE files
** that spiff is doing something
*/
void
Z_chatter(str)
char *str;
{
if (!_Z_qflag)
{
(void) fputs("spiff -- ",stderr);
(void) fputs(str,stderr);
}
}
#endif
/*
** complain unless you've been told to be quiet
*/
void
Z_complain(str)
char *str;
{
if (!_Z_qflag)
(void) fputs(str,stderr);
}
/*
** quit with an error code
*/
static void
_Z_errexit()
{
(void) exit(2);
}
/*
** complain and die
*/
void
_Z_qfatal(str)
char *str;
{
V_cleanup(); /* try reset the device to normal */
O_cleanup(); /* " " " " " " */
Z_complain(str);
_Z_errexit();
}
/*
** scream and die
*/
void
Z_fatal(str)
char *str;
{
V_cleanup(); /* try reset the device to normal */
O_cleanup(); /* " " " " " " */
(void) fputs(str,stderr);
_Z_errexit();
}
/*
** allocate memory with error checking
*/
int*
_Z_myalloc(k)
int k;
{
int *tmp;
if (tmp = (int*) calloc((unsigned)k,(unsigned)1))
{
return(tmp);
}
Z_fatal("Out of Memory\n");
return(tmp); /* boilerplate to shut up lint */
}
void
Z_exceed(d)
int d;
{
(void) sprintf(Z_err_buf,
"The files differ in more than %d places\n", d);
_Z_qfatal(Z_err_buf);
}

49
utils/Spiff/misc.h Normal file
View File

@ -0,0 +1,49 @@
/* 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 Z_INCLUDED
/*
** make sure that if we have a XENIX system, that
** we also treat it as an AT and T derivative
*/
#ifdef XENIX
#ifndef ATT
#define ATT
#endif
#endif
#define Z_LINELEN 1024
#define Z_WORDLEN 20
extern char Z_err_buf[];
/*
** helpful macros
*/
#define Z_ABS(x) (( (x) < (0) )? (-(x)):(x))
#define Z_MIN(x,y) (( (x) < (y) )? (x):(y))
#define Z_MAX(x,y) (( (x) > (y) )? (x):(y))
#define Z_ALLOC(n,type) ((type*) _Z_myalloc((n) * sizeof (type)))
extern int *_Z_myalloc();
/*
** lines needed to shut up lint
*/
extern void Z_complain();
extern void Z_fatal();
extern void Z_exceed();
extern void Z_setquiet();
#ifndef NOCHATTER
extern void Z_chatter();
#endif
#define Z_INCLUDED
#endif

558
utils/Spiff/output.c Normal file
View File

@ -0,0 +1,558 @@
/* 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$";
#endif
#include <stdio.h>
#ifdef M_TERMINFO
#include <curses.h>
#include <term.h>
#endif
#ifdef M_TERMCAP
#ifdef XENIX
#include <tcap.h>
#endif
#endif
#include "misc.h"
#include "flagdefs.h"
#include "edit.h"
#include "line.h"
#include "token.h"
static int _O_need_init = 1;
static int _O_st_ok = 0;
static int _O_doing_ul = 0;
static char *_O_st_tmp;
#ifdef M_TERMCAP
static char _O_startline[Z_WORDLEN];
static char _O_endline[Z_WORDLEN];
#endif
static void
_O_st_init()
{
char termn[Z_WORDLEN];
#ifdef M_TERMCAP
static char entry[1024];
#endif
/*
** see if standard out is a terminal
*/
if (!isatty(1))
{
_O_need_init = 0;
_O_st_ok = 0;
return;
}
if (NULL == (_O_st_tmp = (char*) getenv("TERM")))
{
Z_complain("can't find TERM entry in environment\n");
_O_need_init = 0;
_O_st_ok = 0;
return;
}
(void) strcpy(termn,_O_st_tmp);
#ifdef M_TERMCAP
if (1 != tgetent(entry,termn))
{
Z_complain("can't get TERMCAP info for terminal\n");
_O_need_init = 0;
_O_st_ok = 0;
return;
}
_O_st_tmp = _O_startline;
_O_startline[0] = '\0';
tgetstr("so",&_O_st_tmp);
_O_st_tmp = _O_endline;
_O_endline[0] = '\0';
tgetstr("se",&_O_st_tmp);
_O_st_ok = (strlen(_O_startline) > 0) && (strlen(_O_endline) > 0);
#endif
#ifdef M_TERMINFO
setupterm(termn,1,&_O_st_ok);
#endif
_O_need_init = 0;
}
void
O_cleanup()
{
/*
** this probably isn't necessary, but in the
** name of compeleteness.
*/
#ifdef M_TERMINFO
resetterm();
#endif
}
static void
_O_start_standout()
{
if (_O_need_init)
{
_O_st_init();
}
if (_O_st_ok)
{
#ifdef M_TERMCAP
(void) printf("%s",_O_startline);
#endif
#ifdef M_TERMINFO
vidattr(A_STANDOUT);
#endif
}
else
{
_O_doing_ul = 1;
}
}
static void
_O_end_standout()
{
if (_O_need_init)
{
_O_st_init();
}
if (_O_st_ok)
{
#ifdef M_TERMCAP
(void) printf("%s",_O_endline);
#endif
#ifdef M_TERMINFO
vidattr(0);
#endif
}
else
{
_O_doing_ul = 0;
}
}
static void
_O_pchars(line,start,end)
char *line;
int start,end;
{
int cnt;
for(cnt=start;cnt < end; cnt++)
{
if (_O_doing_ul)
{
(void) putchar('_');
(void) putchar('\b');
}
(void) putchar(line[cnt]);
}
}
/*
** convert a 0 origin token number to a 1 orgin token
** number or 1 origin line number as appropriate
*/
static
_O_con_line(numb,flags,filenum)
int numb, flags,filenum;
{
if (flags & U_TOKENS)
{
return(numb+1);
}
else
{
/*
** check to make sure that this is a real
** line number. if not, then return 0
** on rare occasions, (i.e. insertion/deletion
** of the first token in a file) we'll get
** line numbers of -1. the usual look-up technique
** won't work since we have no lines before than 0.
*/
if (numb < 0)
return(0);
/*
** look up the line number the token and then
** add 1 to make line number 1 origin
*/
return(L_tl2cl(filenum,numb)+1);
}
}
static char *
_O_convert(ptr)
char *ptr;
{
static char spacetext[Z_WORDLEN];
if (1 == strlen(ptr))
{
switch (*ptr)
{
default:
break;
case '\n' :
(void) strcpy(spacetext,"<NEWLINE>");
return(spacetext);
case '\t' :
(void) strcpy(spacetext,"<TAB>");
return(spacetext);
case ' ' :
(void) strcpy(spacetext,"<SPACE>");
return(spacetext);
}
}
return(ptr);
}
static char*
_O_get_text(file,index,flags)
int file,index,flags;
{
static char buf[Z_LINELEN*2]; /* leave lots of room for both
the token text and the
chatter that preceeds it */
char *text;
K_token tmp;
if (flags & U_TOKENS)
{
tmp = K_gettoken(file,index);
text = _O_convert(K_gettext(tmp));
(void) sprintf(buf,"%s -- line %d, character %d\n",
text,
/*
** add 1 to make output start at line 1
** and character numbers start at 1
*/
L_tl2cl(file,K_getline(tmp))+1,
K_getpos(tmp)+1);
return(buf);
}
else
{
return(L_gettline(file,index));
}
}
#define _O_APP 1
#define _O_DEL 2
#define _O_CHA 3
#define _O_TYPE_E 4
static void
_O_do_lines(start,end,file)
int start,end,file;
{
int cnt;
int lastline = -1;
int nextline;
K_token nexttoken;
for (cnt=start;cnt <= end; cnt++)
{
nexttoken = K_get_token(file,cnt);
nextline = K_getline(nexttoken);
if (lastline != nextline)
{
int lastone,lastchar;
K_token lasttok;
char linetext[Z_LINELEN+1]; /* leave room for
terminator */
if (0 == file)
{
(void) printf("< ");
}
else
{
(void) printf("> ");
}
/*
** put loop here if you want to print
** out any intervening lines that don't
** have any tokens on them
*/
/*
** following line is necessary because
** L_gettline is a macro, and can't be passed
*/
(void) strcpy(linetext,L_gettline(file,nextline));
_O_pchars(linetext,0,K_getpos(nexttoken));
_O_start_standout();
/*
** look for last token on this line to be
** highlighted
*/
for ( lastone=cnt,lasttok = K_get_token(file,lastone);
(lastone<=end)&&(nextline == K_getline(lasttok));
lastone++,lasttok = K_get_token(file,lastone))
{
}
lastone--;
lasttok = K_get_token(file,lastone);
lastchar = K_getpos(lasttok)
+ strlen(K_gettext(lasttok));
_O_pchars(linetext,K_getpos(nexttoken),lastchar);
_O_end_standout();
_O_pchars(linetext,lastchar,strlen(linetext));
lastline = nextline;
}
}
}
void
O_output(start,flags)
E_edit start;
int flags;
{
int type = _O_TYPE_E; /* initialize to error state
** this is to make sure that type is set
** somewhere
*/
int t_beg1, t_beg2, t_end1, t_end2; /* token numbers */
int first1, last1, first2, last2;
E_edit ep, behind, ahead, a, b;
/*
** reverse the list of edits
*/
ahead = start;
ep = E_NULL;
while (ahead != E_NULL) {
/*
** set token numbers intentionally out of range
** as boilerplate
*/
t_beg1 = t_beg2 = t_end1 = t_end2 = -1;
/*
** edit script is 1 origin, all of
** our routines are zero origin
*/
E_setl1(ahead,(E_getl1(ahead))-1);
E_setl2(ahead,(E_getl2(ahead))-1);
behind = ep;
ep = ahead;
ahead = E_getnext(ahead);
E_setnext(ep,behind);
}
/*
** now run down the list and collect the following information
** type of change (_O_APP, _O_DEL or _O_CHA)
** start and length for each file
*/
while (ep != E_NULL)
{
b = ep;
/*
** operation always start here
*/
t_beg1 = E_getl1(ep);
/*
** any deletions will appear before any insertions,
** so, if the first edit is an E_INSERT, then this
** this is an _O_APP
*/
if (E_getop(ep) == E_INSERT)
type = _O_APP;
else {
/*
** run down the list looking for the edit
** that is not part of the current deletion
*/
do {
a = b;
b = E_getnext(b);
} while ((b != E_NULL) &&
(E_getop(b) == E_DELETE) &&
((E_getl1(b)) == ((E_getl1(a))+1)));
/*
** if we have an insertion at the same place
** as the deletion we just scanned, then
** this is a change
*/
if ((b != E_NULL) &&
((E_getop(b)) == E_INSERT) &&
((E_getl1(b))==(E_getl1(a))))
{
type = _O_CHA;
}
else
{
type = _O_DEL;
}
/*
** set up start and length information for
** first file
*/
t_end1 = E_getl1(a);
/*
** move pointer to beginning of insertion
*/
ep = b;
/*
** if we are showing only a deletion,
** then we're all done, so skip ahead
*/
if (_O_DEL == type)
{
t_beg2 = E_getl2(a);
t_end2 = -1; /* dummy number, won't
ever be printed */
goto skipit;
}
}
t_beg2 = E_getl2(ep);
t_end2 = t_beg2-1;
/*
** now run down the list lookingfor the
** end of this insertion and keep count
** of the number of times we step along
*/
do {
t_end2++;
ep = E_getnext(ep);
} while ((ep != E_NULL) && ((E_getop(ep)) == E_INSERT) &&
((E_getl1(ep)) == (E_getl1(b))));
skipit:;
if (flags & U_TOKENS)
{
/*
** if we are dealing with tokens individually,
** then just print then set printing so
*/
first1 = t_beg1;
last1 = t_end1;
first2 = t_beg2;
last2 = t_end2;
}
else
{
/*
** we are printing differences in terms of lines
** so find the beginning and ending lines of the
** changes and print header in those terms
*/
if ( t_beg1 >= 0)
first1 = K_getline(K_get_token(0,t_beg1));
else
first1 = t_beg1;
if ( t_end1 >= 0)
last1 = K_getline(K_get_token(0,t_end1));
else
last1 = t_end1;
if ( t_beg2 >= 0)
first2 = K_getline(K_get_token(1,t_beg2));
else
first2 = t_beg2;
if ( t_end2 >= 0)
last2 = K_getline(K_get_token(1,t_end2));
else
last2 = t_end2;
}
/*
** print the header for this difference
*/
(void) printf("%d",_O_con_line(first1,flags,0));
switch (type)
{
case _O_APP :
(void) printf("a%d",_O_con_line(first2,flags,1));
if (last2 > first2)
{
(void) printf(",%d",_O_con_line(last2,flags,1));
}
(void) printf("\n");
break;
case _O_DEL :
if (last1 > first1)
{
(void) printf(",%d",_O_con_line(last1,flags,0));
}
(void) printf("d%d\n",_O_con_line(first2,flags,1));
break;
case _O_CHA :
if (last1 > first1)
{
(void) printf(",%d",_O_con_line(last1,flags,0));
}
(void) printf("c%d",_O_con_line(first2,flags,1));
if (last2 > first2)
{
(void) printf(",%d",_O_con_line(last2,flags,1));
}
(void) printf("\n");
break;
default:
Z_fatal("type in O_output wasn't set\n");
}
if (_O_DEL == type || _O_CHA == type)
{
if (flags & U_TOKENS)
{
int cnt;
for(cnt=first1;cnt <= last1; cnt++)
{
(void) printf("< %s",
_O_get_text(0,cnt,flags));
}
}
else
{
_O_do_lines(t_beg1,t_end1,0);
}
}
if (_O_CHA == type)
{
(void) printf("---\n");
}
if (_O_APP == type || _O_CHA == type)
{
if (flags & U_TOKENS)
{
int cnt;
for(cnt=first2;cnt <= last2; cnt++)
{
(void) printf("> %s",
_O_get_text(1,cnt,flags));
}
}
else
{
_O_do_lines(t_beg2,t_end2,1);
}
}
}
O_cleanup();
return;
}

17
utils/Spiff/output.h Normal file
View File

@ -0,0 +1,17 @@
/* 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 O_INCLUDED
extern void O_output();
extern void O_cleanup();
#define O_INCLUDED
#endif

819
utils/Spiff/paper.ms Normal file
View File

@ -0,0 +1,819 @@
.ll 6i
.nr PO 1.15i
.nr HM 0i
.nr FM 1.05i
.TL
SPIFF -- A Program for Making Controlled Approximate Comparisons of Files
.AU
Daniel Nachbar
.AI
Software Engineering Research Group
Bell Communications Research
Morristown, New Jersey
.AB
The well known program
.B
diff
.R
[1]
is inappropriate for some
common tasks such as comparing the output of floating
point calculations where roundoff errors
lead
.B
diff
.R
astray and comparing program source code
where some differences in the text (such as white space and comments)
have no effect on the operation of the compiled code. A new program,
named
.B
spiff,
.R
addresses these and other similar cases
by lexical parsing of the input files and then applying
a differencing algorithm to the token sequences.
.B
Spiff
.R
ignores differences
between floating point numbers that are below a user settable tolerance.
Other features include user settable commenting and literal string
conventions and a choice of differencing algorithm.
There is also an interactive mode wherein the input texts are displayed
with differences highlighted. The user can change numeric tolerances
"on the fly" and
.B
spiff
.R
will adjust the highlighting accordingly.
.AE
.SH
Some Troubles With Diff
.PP
Over the past several years, it has been fairly easy to tell when
a new type of computer arrived at a nearby computer center.
The best clue was the discordant chorus of
groaning, sighing, gnashing of teeth, pounding of foreheads on desks,
and other sounds of distress. Tracing these noises to their source, one
would find some poor soul in the process of installing
a numerical analysis package on the new machine.
.PP
One might expect that "moving up" to a new machine
would be a cause for celebration.
After all, new machines are typically bigger, faster,
and better than old machines.
However, the floating point arithmetic on any new machine is frequently
slightly different from any old machine.
As a consequence,
software package test routines produce output that is slightly different,
but still correct, on the new machines.
Serious troubles appear when the person installing the software package
attempts to compare the test output files from two different machines
by using a difference finding program such as
.B
diff.
.R
Programs such as
.B
diff
.R
do a character by character comparison.
.B
Diff
.R
finds a great many differences, most of which
are due to roundoff errors in the least significant digits of floating point
numbers. Others are the result of differences in the way
in which the two test runs
had printed a number (3.4e-1 vs. 0.34).
In one case, the test suite for the S statistical analysis package[2],
over 1700 floating point numbers are produced
(per machine). In the eyes of
.B
diff,
.R
roughly 1200 of these numbers are different.
However, none of the "differences" are important ones.
Nonetheless, software installers wind up inspecting the output by eye.
.PP
A similar problem arises when one attempts to
look for differences between two versions
of the same C program.
.B
Diff
.R
reports many differences that are not of interest. In
particular, white space (except inside quotation marks) and
anything inside a comment have no effect on the operation of the compiled
program and are usually not of interest.
.B
Diff
.R
does have a mode of operation where white space
within a line (spaces and tabs) can be ignored.
However, differences in the placement of newlines cannot be ignored.
This is particularly annoying since C programming
styles differ on whether to place a newline character before or after the '{'
characters that start blocks.
.SH
The Problem in General Terms
.PP
As already mentioned, programs such as
.B
diff
.R
do
a character-by-character comparison of the input files.
However, when it comes to interpreting
the contents of a file (either by a human or by a program)
it is almost never the case that characters
are treated individually. Rather, characters make up tokens such
as words and numbers, or act as separators between these tokens.
When comparing files, one is usually looking for
differences between these tokens, not the characters that make them up
or the characters that separate them.
.PP
What is needed is a program that first parses the input files
into tokens, and then applies a differencing algorithm to the token
sequences.
In addition to finding differences in terms of tokens,
it is possible to interpret the tokens and
compare different types of tokens in different ways. Numbers, for example,
can differ by a lot or a little.\**
.FS
Current differencing programs do not have such a notion because
the difference between two characters is a binary function.
Two characters are the same or they are not.
.FE
It is possible to use a tolerance when comparing two number tokens and
report only those differences that exceed the tolerance.
.SH
Design Issues
.PP
A serious design issue for such a program is how
complex to make the parse. The
.I
deeper
.R
one goes in the parsing the larger
the unit of text that can be manipulated. For instance, if one is looking
for differences in C code, a complete parse tree can be produced and
the differencing algorithm could examine insertion and deletion of entire
branches of the tree. However, deep parsing requires much more
complex parsing and slower differencing algorithms.
.PP
Another design issue is deciding how to interpret the tokens.
Closer interpretation may lead to greater flexibility in comparing tokens, but
also results in a more cumbersome and error-prone implementation.
.PP
In the program described here, we attempt to keep both the depth
of the parse and the semantics of the tokens to a minimum.
The parse is a simple
lexical parse with the input files broken up into one dimensional
sequences of numbers, literal strings and white space.
Literal strings and white space are not interpreted. Numbers
are treated as representing points on the real number line.
.SH
Default Operation
.PP
.B
Spiff\**
.R
.FS
We picked the name as a way to pay a small tribute to that famous intergalactic
adventurer Spaceman Spiff[3].
.B
Spiff
.R
is also a contraction of "spiffy diff".
.FE
works very much like
.B
diff.
.R
It reads two files, looks
for differences, and prints a listing of the
differences in the form of
an edit script.\**
.FS
An edit script is a sequence of insertions and deletions
that will transform the first file into the second.
.FE
As already suggested,
.B
spiff
.R
parses the files into
literal strings and real numbers.
The definition of these tokens can be altered somewhat by the user
(more on this later). For now, suffice it
to say that literals are strings like "cow", "sit",
"into", etc. Real numbers look like "1.3", "1.6e-4" and so on.
All of the common formats for real numbers are recognized.
The only requirements for a string to be
treated as a real number is the presence
of a period and at least one digit.
By default, a string of digits without a decimal point
(such as "1988") is not considered to be a real number,
but rather a literal string.\**
Each non-alphanumeric character (such as #$@^&*)
is parsed into a separate literal token.
.FS
Integer numbers are often used as indices, labels, and so on.
Under these circumstances, it is more appropriate to treat them as literals.
Our choice of default was driven by a design goal
of having
.B
spiff
.R
be very conservative
when choosing to ignore differences.
.FE
.PP
Once
.B
spiff
.R
determines the two sequences of tokens,
it compares members of the first sequence with
members of the second sequence.
If two tokens are of different types,
.B
spiff
.R
deems them to be different, regardless of their content.
If both tokens are literal tokens,
.B
spiff
.R
will deem them
to be different if any of their characters differ.
When comparing two real numbers,
.B
spiff
.R
will deem them to be different only if
the difference in their values exceeds a user settable tolerance.
.SH
Altering Spiff's Operation
.PP
To make
.B
spiff
.R
more generally useful, the user can control:
.IP \(bu
how text strings are parsed into tokens
.IP \(bu
how tokens of the same type are compared
.IP \(bu
the choice of differencing algorithm used
.IP \(bu
and the granularity of edit considered by the differencing algorithm.
.LP
.PP
These features are described next.
.SH
Altering the Parse
.PP
The operation of the parser can be altered in several ways.
The user can specify that delimited sections of text are to be ignored
completely. This is useful for selectively ignoring the contents of
comments in programs. Similarly, the user can specify that
delimited sections of text (including white space)
be treated as a single literal token. So, literal strings in program
text can be treated appropriately.
Multiple sets of
delimiters may be specified at once (to handle cases such as the
Modula-2 programming language
where there are two ways to specify quoted strings). At present,
the delimiters must be fixed string (possibly restricted to the
beginning of the line) or end of line.
As a consequence of the mechanism for specifying literal strings,
multicharacter operators (such as the += operator in C)
can be parsed into a single token.
.PP
As yet, no provision is made for allowing delimiter
specification in terms of regular expressions. This omission
was made for the sake of simplifying the parser.
Nothing prevents the addition of regular expressions in the
future. However, the simple mechanism
already in place handles the literal string and commenting conventions
for most well known programming languages.\**
.FS
See the manual page in the appendix for examples of handling
C, Bourne Shell, Fortran, Lisp, Pascal, and Modula-2. The only
cases that are known not to work are comments in BASIC and
Hollerith strings in Fortran.
.FE
.PP
In addition to controlling literal string and comments, the user
may also specify whether to treat white space characters as any other
non-alphanumeric character (in other words, parse each white space
character into its own literal token),
whether to parse sign markers as part
of the number that they precede or as separate tokens, whether
to treat numbers without printed decimal markers (e.g. "1988")
as real numbers rather than as literal strings, and whether
to parse real numbers into literal tokens.
.SH
Altering the Comparison of Individual Tokens
.PP
As mentioned earlier, the user can set a tolerance below which differences
between real numbers are ignored.
.B
Spiff
.R
allows two kinds of tolerances:
absolute and relative.
Specifying an absolute tolerance will cause
.B
spiff
.R
to ignore differences
that are less than the specified value.
For instance, specifying an absolute tolerance of 0.01 will
cause only those differences greater than or equal to 0.01 to be reported.
Specifying a relative tolerance will cause
.B
spiff
.R
to ignore differences that are
smaller than some fraction of the number of larger magnitude.
Specifically, the value of the tolerance is interpreted
as a fraction of the larger (in absolute terms)
of the two floating point numbers being compared.
For example,
specifying a relative tolerance of 0.1
will cause the two floating point numbers 1.0 and 0.91 to be deemed within
tolerance. The numbers 1.0 and 0.9 will be outside the tolerance.
Absolute and relative tolerances can be OR'ed together. In fact,
the most effective way to ignore differences that are due to roundoff errors
in floating point calculations is to use both
a relative tolerance (to handle limits in precision) as well as an absolute
tolerance (to handle cases when one number is zero and the other number is
almost zero).\**
.FS
All numbers differ from zero by 100% of their magnitude. Thus, to handle
numbers that are near zero, one would have to specify a relative tolerance
of 100% which would be unreasonably large when both numbers are non-zero.
.FE
In addition, the user can specify an infinite tolerance. This is useful
for checking the format of output while ignoring the actual numbers
produced.
.SH
Altering the Differencing Algorithm
.PP
By default,
.B
spiff
.R
produces a minimal edit sequence (using the Miller/Myers differencing algorithm[4])
that will convert the first file into the second.
However, a minimal edit sequences is not always desirable.
For example, for the following two tables of numbers:
.DS
0.1 0.2 0.3 0.2 0.3 0.4
0.4 0.5 0.6 0.5 0.6 0.7
.DE
a minimal edit sequence to convert the table on
the left into the table on the right be to
would delete the first number (0.1) and insert 0.7 at the end.\**
.FS
The problem of having the elements of tables become misaligned when
the differencing algorithm is trying
to find a minimal number of edits can be reduced somewhat
by retaining newlines and not using tolerances.
Unfortunately, it does not go away.
.FE
Such a result, while logically correct, does not provide a good picture
of the differences between the two files.
In general, for text with a very definite structure (such as tables),
we may not want to consider insertions and deletions at all, but
only one-to-one changes.\**
.FS
A "change" can be expressed as one deletion and one insertion at the same
point in the text.
.FE
So, rather than look for a minimal edit script, we
merely want to compare each token in the first file with
the corresponding token in the second file.
.PP
The user can choose which differencing algorithm to use
(the default Miller/Myers or
the alternative one-to-one comparison)
based upon what is known about the input files. In general,
files produced mechanically
(such the output from test suites) have a very regular structure
and the one-to-one comparison works surprisingly well.
For files created by humans, the Miller/Myers
algorithm is more appropriate.
There is nothing in
.B
spiff's
.R
internal design that limits
the number of differencing algorithms that it can run.
Other differencing algorithms,
in particular the one used in
.B
diff,
.R
will probably be added later.
.SH
Altering the Granularity of the Edit Sequence
.PP
By default,
.B
spiff
.R
produces an edit sequence
in terms of insertions and deletions of individual tokens.
At times it may be more useful to
treat the contents of the files as tokens when looking for differences
but
express the edit script in terms of entire lines of the files rather
than individual tokens.\**
.FS
For instance, if one wants to have
.B
spiff
.R
produce output that can be fed into
the
.B
ed
.R
editor.
.FE
.B
Spiff
.R
provides a facility for restricting the edits to entire lines.
.SH
Treating Parts of the Files Differently
.PP
For complex input files, it is important that different parts of the
file be treated in different ways. In other words, it may be impossible
to find one set of parsing/differencing rules that work well for the
entire file.
.B
Spiff
.R
can differentiate between parts of the input files on two bases:
within a line and between lines.
Within a line, a different tolerance can be applied to each real number.
The tolerances are specified in terms of the ordinal position of the
numbers on the line (i.e. one tolerance is applied to the first real number
on each line, a different tolerance is applied to the second number on
each line, a third tolerance is applied to the third, and so on). If more
numbers appear on a line than there are tolerances specified, the last
tolerance is applied to all subsequent numbers on the line (i.e., if the user
specifies three tolerances, the third is applied to the third, fourth
fifth, . . . number on each line). This feature is useful for applying
different tolerances to the different columns of a table of numbers.
.PP
Between lines, the user can place "embedded commands" in the input files.
These commands
are instructions to parser that can change what tolerances are attached
to real numbers and the commenting and literal string conventions used by the
parser. Embedded commands are flagged to the parser
by starting the line with a user-specified
escape string. By combining within line and between line differentiation,
it is possible for the user to specify a different tolerance
for every single real number in the input files.
.SH
Visual Mode
.PP
So far,
.B
spiff's
.R
operation as an intelligent filter has been described.
.B
Spiff
.R
also has an interactive mode.
When operating in interactive mode,
.B
spiff
.R
places corresponding sections of the input files
side by side on user's screen.\**
.FS
Although the current implementation of
.B
spiff
.R
runs in many environments,
interactive mode works only under the MGR window manager.[5]
Other graphics interfaces will probably be added over time.
.FE
Tokens are compared using a one-to-one ordinal comparison, and any tokens that
are found to be different are highlighted in reverse video.
The user can interactively change the tolerances and
.B
spiff
.R
will alter the display
to reflect which real numbers exceed the new tolerances.
Other commands allow the user to page through the file and exit.
.SH
Performance
.PP
Two components of
.B
spiff,
.R
the parser and the differencing algorithm,
account for most of the execution time. Miller and Myers compare their
algorithm to the one used in the diff program. To restate their results,
the Miller/Myers algorithm is faster for files
that have relatively few differences but much
slower (quadratic time) for files with a great many differences.
.PP
For cases where the files do not differ greatly,
parsing the input files takes most of the time (around 80% of the total).\**
.FS
No effort has yet been made to make the parser run more quickly.
A faster parser could no doubt be written by generating a special state machine.
.FE
The performance of the parser is roughly similar to programs that do a similar
level of parsing (i.e. programs that must examine each character in the file).
For files where roughly half of the tokens are real numbers,
.B
spiff
.R
takes about twice as long to parse the input files
as an
.B
awk
.R
program that counts the number of words in a file:\**
.FS
For
.B
awk,
.R
a word is any string separated by white space.
.FE
.B
.DS
awk '{total += NF}' firstfile secondfile
.DE
.R
.PP
The time that it takes
.B
spiff
.R
to parse a file is substantially
increased if scanning is done for comments
and delimited literal strings. The precise effect depends upon the length of
the delimiters, whether they are restricted to appear at beginning of line, and
the frequency with which literals and comments appear in the input files.
As an example, adding the 12 literal conventions\**
.FS
One literal convention is for C literal strings. The rest enumerate multicharacter
operators.
.FE
and 1 commenting convention
required for C code roughly doubles the time required to parse input files.\**
.FS
So in total, it takes
.B
spiff
.R
about 4 times longer to parse a C program than it takes
.B
awk
.R
to count the number of words in the same file.
.FE
.PP
A more complete approach to evaluating
.B
spiff's
.R
performance must measure the total time that it takes for the user to complete a
differencing task. For example, consider one of the
test suites for the S statistical
analysis package mentioned at the beginning of this paper.
The output file for each machine is 427 lines long and contains
1090 floating point numbers. It takes
.B
diff
.R
approximately 2 seconds on one of our "6 MIPS"\** computers
.FS
We will not comment on the usefulness of "MIPS" as a measure
of computing speed. The numbers provided are only intended to
give the reader some vague idea of how fast these programs run.
.FE
to compare the two files and produce
an edit script that is 548 lines long containing 1003 "differences"
in the floating point numbers. It takes the average tester
5 minutes to print out the edit script and roughly 2 hours to examine
the output by hand to determine that the machines are, in fact,
both giving nearly identical answers. The total time needed is
2 hours 5 minutes and 2 seconds.
.PP
In contrast, it takes
.B
spiff
.R
approximately 6 seconds on one of our "6 MIPS" computers to
produce an output file that is 4 lines long.\**
.FS
The output would be zero length except that the output of the
.B
time
.R
command is built into the S tests.
The timing information could easily be ignored using
.B
spiff's
.R
embedded commands. But, as we shall see, it hardly seems worth the trouble.
.FE
It takes the average tester 30 seconds to examine
.B
spiff's
.R
output. The total for
.B
spiff
.R
is 36 seconds. Therefore for this case,
.B
spiff
.R
will get the job done roughly 208.88 times faster than
.B
diff.
.R
.PP
In general, it is misleading to compare
.B
spiff's
.R
speed with that of
.B
diff.
.R
While both programs are looking for differences between files,
they operate on very different types of data (tokens vs. bytes).
An analogous comparison could be made between the speed of an assembler
and the speed of a C compiler. They are both language translators.
One runs much faster than the other.
None the less, most programmers use the slower program
whenever possible.
.SH
Using Spiff For Making Regression Tests Of Software
.PP
We envision
.B
spiff
.R
to be the first of several tools for aiding in the now
arduous task of making regression tests.\**
.FS
In software engineering parlance, a "regression test" is the process by
which a tester checks to make sure that the new version of a piece of
software still performs the same way as the older versions
on overlapping tasks.
.FE
Given
.B
spiff's
.R
current capabilities, the regression test designer can
take the output of an older version of software and through
the use of literal string and commenting conventions,
specify what parts of the output must remain identical and
what sections can change completely. By specifying tolerances, the test
designer can take into account how much of a difference in floating
point calculations is acceptable.
.PP
The test designer is also free to
edit the output from the older version of the software and add embedded
commands that can instruct
.B
spiff
.R
to treat various parts of the output
differently. The newly edited output can then serve as a template for
the output of later versions of the software.
.PP
Obviously, editing output by hand is a very low level mechanism for adding
specification information. It is our intention that
.B
spiff
.R
will become
the last element in a pipeline of programs. Programs (as yet unwritten) located
earlier in the pipeline
can implement a higher level representation of the specification information.
They read in the old and new input files, add the appropriate embedded commands,
and then pass the results to
.B
spiff
.R
which will do the actual differencing.
.SH
Future Work
.PP
There are many features that could be added to
.B
spiff
.R
(if there are not
too many already). Some of these include:
.IP \(bu
Using separate differencing algorithms on separate sections of the file
and/or limiting the scope of an edit sequence (fencing)
.IP \(bu
Providing a more general mechanism for specifying comments and literals
(perhaps allowing specification in terms of regular expressions).
As yet, we have not encountered any important cases where regular expressions
have been needed. Until such a case is encountered, we will leave regular
expressions out in the name of simplicity.
.IP \(bu
Allowing for a more general specification of what lines should look like.
At present, the user can only specify tolerances for numbers as a function
of their ordinal position on a line. The difficulty in expanding the
specification abilities of
.B
spiff
.R
is knowing when to stop. In the extreme,
we might add all of the functionality of a program such as
.B
awk.\**
.R
.FS
Imagine handling the case such as
"apply this tolerance to all numbers that appear
on a line starting with the word `foo' but only if the number is between 1.9
and 3.6 and the word `bar' does not appear on the line".
.FE
We hope to keep
.B
spiff
.R
as simple as possible. Our first efforts in
this direction will try to implement higher level specification functions
outside of
.B
spiff.
.R
.SH
Acknowledgements
.PP
First and foremost, we thank Stu Feldman for his endless patience, constant encouragement
and numerous good ideas. We also extend thanks to Doug McIlroy for bringing the Miller/Myers
algorithm to our attention, Nat Howard for a key insight
and for his editorial comments
and Steve Uhler and Mike Bianchi for their editorial comments.
.SH
References
.IP [1]
Hunt,J.W. and M.D. McIlroy.
.I
An Algorithm For Differential File Comparisons,
.R
.B
Bell Labs Computer Science Technical Report,
.R
Number 41, 1975.
.IP [2]
Becker,R.A. and J.M. Chambers (1984).
.B
S \- An Interactive Environment For Data Analysis And
Graphics.
.R
Belmont, CA: Wadsworth Inc.
.IP [3]
Watterson, B. (1987).
.B
Calvin and Hobbes.
.R
New York: Andrews, McMeel & Parker.
.IP [4]
Miller, W. and E.W. Myers.
.I
A File Comparison Program,
.R
.B
Software \-
Practice and Experience
.R
15, 11, 1025-1040, 1985.
.IP [5]
Uhler, S.A.
.I
MGR -- A Window Manager For UNIX,
.R
Sun User's Group Meeting. September 1986.
.LP

802
utils/Spiff/parse.c Normal file
View File

@ -0,0 +1,802 @@
/* 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$";
#endif
#include "misc.h"
#include "flagdefs.h"
#include "float.h"
#include "tol.h"
#include "token.h"
#include "line.h"
#include "command.h"
#include "comment.h"
#include "parse.h"
#include <ctype.h>
#define _P_PARSE_CHATTER 1000
static int _P_realline; /* loop counter */
static int _P_fnumb;
static char *_P_nextchr; /* pointer to the next character to parse */
static char *_P_firstchr; /* pointer to the beginning of the line being parsed */
static int _P_next_tol; /* number of floats seen on this line */
static int _P_stringsize; /* count of number of characters that are being
read into a comment or literal */
static int _P_has_content; /* flag to indicate if the line being
parsed has any tokens on it */
static int _P_start; /* first line to parse */
static int _P_lcount; /* number of lines to parse */
static int _P_flags; /* location for global flags */
/*
** by default, "words" can be made up of numbers and letters
** the following code allows for extending the alphabet that can
** be used in words. this is useful for handling languages such
** as C where the underscore character is an allowable character
** in an identifier. If a character (such as underscore) is NOT added
** to the alphabet, the identifier will be broken into 2 or more "words"
** by the parser. as such the two sequences
** one_two
** and
** one _ two
** would look identical to spiff.
*/
#define _P_ALPHALEN 256
static char _P_alpha[_P_ALPHALEN];
static void
_P_alpha_clear()
{
*_P_alpha = '\0';
}
static
_P_in_alpha(chr)
char chr;
{
#ifndef ATT
extern int index();
#endif
/*
** special case when string terminator
** is handed to us
*/
if ('\0' == chr)
return(0);
#ifdef ATT
return((int) strchr(_P_alpha,chr));
#else
return((int) index(_P_alpha,chr));
#endif
}
void
P_addalpha(ptr)
char *ptr;
{
char buf[Z_LINELEN];
S_wordcpy(buf,ptr); /* copy up to (but not including)
the first whitespace char */
if ((strlen(_P_alpha) + strlen(buf)) >= _P_ALPHALEN)
{
Z_fatal("too many characters added to extended alphabet");
}
(void) strcat(_P_alpha,buf);
}
/*
** put parser in a default state
*/
static char _P_dummyline[2]; /* a place to aim wild pointers */
static void
_P_initparser()
{
_P_dummyline[0] = '\0';
/*
** now reset all the state of each module
*/
C_clear_cmd(); /* disable embedded command key word */
T_clear_tols();
W_clearcoms();
W_clearlits();
_P_alpha_clear(); /* disable extended alphabet */
/*
** and set state as defined by execute-time commands.
*/
C_docmds();
return;
}
static
_P_needmore()
{
return(*_P_nextchr == '\0');
}
static
_P_nextline()
{
/*
** if the line that we just finished had
** some content, increment the count
*/
if (_P_has_content)
{
L_incclmax(_P_fnumb);
/*
** if the previous line had a token
** increment the line
*/
if (L_getcount(_P_fnumb,L_gettlmax(_P_fnumb)))
{
L_inctlmax(_P_fnumb);
L_setcount(_P_fnumb,L_gettlmax(_P_fnumb),0);
}
_P_has_content = 0;
}
/*
** reset the number of floats seen on the line
*/
_P_next_tol = 0;
/*
** get another line if there is one available
*/
_P_realline++;
if (_P_realline >= _P_start+_P_lcount)
{
return(1);
}
_P_firstchr = _P_nextchr = L_getrline(_P_fnumb,_P_realline);
/*
** and look for a command
*/
if (C_is_cmd(_P_firstchr))
{
_P_nextchr = _P_dummyline;
_P_has_content = 0;
}
else
{
/*
** we have a real line, so set up the index
*/
L_setclindex(_P_fnumb,L_getclmax(_P_fnumb),_P_realline);
_P_has_content = 1;
}
return(0);
}
/*
** the following three routines (_P_litsnarf, _P_bolsnarf, and _P_comsnarf
** all do roughly the same thing. they scan ahead and collect the
** specified string, move _P_nextchr to the end of the
** comment or literal and return 1 if we run off the end of file,
** 0 otherwise. it would have been nice to have 1 routine handle
** all three task (there is much common code), however there were
** so enough differences, (for instance, only comments check for nesting,
** only literals need to set _P_stringsize, etc)
** that I decided to split them up.
*/
static int
_P_litsnarf(litptr)
W_lit litptr;
{
_P_stringsize = 0;
/*
** skip the start of literal string
*/
_P_nextchr += strlen(W_litbegin(litptr));
_P_stringsize += strlen(W_litbegin(litptr));
/*
** is there a separate end string?
** if not, then we're done
*/
if ('\0' == *(W_litend(litptr)))
{
return(0);
}
/*
** loop once for each character in the literal
*/
while(1)
{
/*
** if we are out of characters, move on to next line
*/
if (_P_needmore())
{
if (_P_nextline())
{
return(1);
}
if (!_P_has_content)
{
/*
** since we've just gotten a command
** check to see if this literal
** is still legit ...
** could have just been reset
** by the command
*/
if (!W_is_lit(litptr))
{
return(0);
}
}
} /* if _P_needmore */
/*
** see if we have an escaped end of literal string
*/
if (('\0' != *(W_litescape(litptr))) && /* escape string exists */
!S_wordcmp(_P_nextchr,
W_litescape(litptr)) && /* and escape matches */
!S_wordcmp(_P_nextchr+strlen(W_litescape(litptr)),
W_litend(litptr))) /* and endstring matches */
{
_P_nextchr += strlen(W_litescape(litptr))
+ strlen(W_litend(litptr));
_P_stringsize += strlen(W_litescape(litptr))
+ strlen(W_litend(litptr));
continue;
}
/*
** see if we have an end of literal string
*/
if (!S_wordcmp(_P_nextchr,W_litend(litptr))) /* escape matches */
{
_P_nextchr += strlen(W_litend(litptr));
_P_stringsize += strlen(W_litend(litptr));
return(0);
}
/*
** this must be yet another character in the literal, so
** just snarf it up
*/
_P_nextchr++;
_P_stringsize++;
} /* while loop once for each character */
#ifndef lint
Z_fatal("shouldn't execute this line at the end of _P_litsnarf");
#endif
} /* _P_litsnarf */
static int
_P_bolsnarf(bolptr)
W_bol bolptr;
{
/*
** skip the start of comment string
*/
_P_nextchr += strlen(W_bolbegin(bolptr));
/*
** is there a separate end string
** if not, then we're done
*/
if ('\0' == *(W_bolend(bolptr)))
{
return(0);
}
/*
** loop once for each character in the comment
*/
while(1)
{
/*
** if we are out of characters,move on to next line
*/
if (_P_needmore())
{
if (_P_nextline())
{
return(1);
}
if (!_P_has_content)
{
/*
** since we've just gotten a command
** check to see if this comment
** is still legit ... comments
** could have just been reset
** by the command
*/
if (!W_is_bol(bolptr))
{
return(0);
}
}
} /* if at end of line */
/*
** see if we have an escaped end of comment string
*/
if ('\0' != *(W_bolescape(bolptr)) && /* escape string exists */
!S_wordcmp(_P_nextchr,
W_bolescape(bolptr)) && /* and escape matches */
!S_wordcmp(_P_nextchr+strlen(W_bolescape(bolptr)),
W_bolend(bolptr))) /* and end string matches */
{
_P_nextchr += strlen(W_bolescape(bolptr))
+ strlen(W_bolend(bolptr));
continue;
}
/*
** see if we have an end of comment string
*/
if (!S_wordcmp(_P_nextchr,W_bolend(bolptr)))
{
_P_nextchr += strlen(W_bolend(bolptr));
return(0);
}
/*
** this must be yet another character in the comment, so
** just snarf it up
*/
_P_nextchr++;
} /* while loop once for each character */
#ifndef lint
Z_fatal("shouldn't execute this line in at end of _P_bolsnarf");
#endif
} /* _P_bolsnarf */
/*
** pass over a comment -- look for nexting
*/
static
_P_comsnarf(comptr)
W_com comptr;
{
int depth = 1; /* nesting depth */
/*
** skip the start of comment string
*/
_P_nextchr += strlen(W_combegin(comptr));
/*
** is there a separate end string
** if not, then we're done
*/
if ('\0' == *(W_comend(comptr)))
{
return(0);
}
/*
** loop once for each character in the comment
*/
while(1)
{
/*
** if we are out of characters, move on to next line
*/
if (_P_needmore())
{
if (_P_nextline())
{
return(1);
}
if (!_P_has_content)
{
/*
** since we've just gotten a command
** check to see if this comment
** is still legit ... comments
** could have just been reset
** by the command
*/
if (!W_is_com(comptr))
{
return(0);
}
}
} /* if at end of line */
/*
** see if we have an escaped end of comment string
*/
if ('\0' != *(W_comescape(comptr)) && /* escape string exists */
!S_wordcmp(_P_nextchr,
W_comescape(comptr)) && /* and escape matches */
!S_wordcmp(_P_nextchr+strlen(W_comescape(comptr)),
W_comend(comptr))) /* and end string matches */
{
/*
** skip over the escape sequence and the end sequence
*/
_P_nextchr += strlen(W_comescape(comptr))
+ strlen(W_comend(comptr));
continue;
}
/*
** see if we have an end of comment string
*/
if (!S_wordcmp(_P_nextchr,W_comend(comptr))) /* end matches */
{
/*
** skip over the end sequence
*/
_P_nextchr += strlen(W_comend(comptr));
if (W_is_nesting(comptr))
{
depth--;
if (0 == depth)
return(0);
}
else
{
return(0);
}
continue;
}
/*
** see if we have another beginning of comment string
*/
if (W_is_nesting(comptr) &&
!S_wordcmp(_P_nextchr,W_comend(comptr))) /* end matches */
{
_P_nextchr += strlen(W_comend(comptr));
depth++;
continue;
}
/*
** this must be yet another character in the comment, so
** just snarf it up
*/
_P_nextchr++;
} /* while loop once for each character */
#ifndef lint
Z_fatal("should not execute this line in _P_comsnarf\n");
#endif
} /* _P_comsnarf */
/*
** parse a file
*/
static void
_P_do_parse()
{
char *ptr; /* scratch space */
int tmp;
int ret_code;
K_token newtoken;
W_bol bolptr;
W_com comptr;
W_lit litptr;
int startline, endline, startpos;
/*
** main parsing loop
*/
while (1)
{
/*
** get more text if necessary
*/
if (_P_needmore())
{
if (_P_nextline())
{
return;
}
/*
** if the line contains nothing of interest,
** try again
*/
if (!_P_has_content)
{
continue;
}
/*
** check to see if this line starts a comment
*/
if ((bolptr = W_isbol(_P_firstchr)) != W_BOLNULL)
{
if (_P_bolsnarf(bolptr))
{
return;
}
continue;
}
} /* if _P_needmore */
/*
** skip whitespace
*/
if (!(U_INCLUDE_WS & _P_flags) && isspace(*_P_nextchr))
{
_P_nextchr++;
continue;
}
/*
** check to see if this character starts a comment
*/
if ((comptr = W_iscom(_P_nextchr)) != W_COMNULL)
{
if (_P_comsnarf(comptr))
{
return;
}
continue;
}
/*
** if there aren't any tokens on this line already
** set up the index from the token line to the content line
*/
if (!L_getcount(_P_fnumb,L_gettlmax(_P_fnumb)))
{
L_settlindex(_P_fnumb,
L_gettlmax(_P_fnumb),
L_getclmax(_P_fnumb));
/*
** and the pointer from the token line to the
** first token on the line
*/
L_setindex(_P_fnumb,
L_gettlmax(_P_fnumb),
K_gettmax(_P_fnumb));
}
startline = L_tl2cl(_P_fnumb,L_gettlmax(_P_fnumb));
startpos = _P_nextchr-_P_firstchr;
newtoken = K_maketoken();
K_setline(newtoken,L_gettlmax(_P_fnumb));
K_setpos(newtoken,startpos);
ret_code = 0;
/*
** check to see if this character starts a
** delimited literal string
*/
if ((litptr = W_islit(_P_nextchr)) != W_LITNULL)
{
ret_code = _P_litsnarf(litptr);
K_settype(newtoken,K_LIT);
S_allocstr(&ptr,_P_stringsize);
/*
** fixed nasty memory bug here by adding else
** old code copied entire line even if literal
** ended before the end of line
** should check into getting strcpy loaded
** locally
*/
endline = L_getclmax(_P_fnumb);
if (endline > startline)
{
/*
** copy in the first line of the literal
*/
(void) strcpy(ptr,
L_getcline(_P_fnumb,startline)
+startpos);
/*
** now copy all the lines between
** the first and last
*/
for (tmp=startline+1;tmp<endline;tmp++)
{
(void) strcat(ptr,
L_getcline(_P_fnumb,tmp));
}
/*
** and now copy in the last line
*/
(void) strncat(ptr,
L_getcline(_P_fnumb,endline),
_P_stringsize-strlen(ptr));
}
else
{
(void) strncpy(ptr,
L_getcline(_P_fnumb,startline)
+startpos,
_P_stringsize);
/*
** terminate the string you just copied
*/
ptr[_P_stringsize] = '\0';
}
K_settext(newtoken,ptr);
} /* if is_lit */
/*
** see if this is a floating point number
*/
else if (tmp = F_isfloat(_P_nextchr,
_P_flags & U_NEED_DECIMAL,
_P_flags & U_INC_SIGN))
{
K_saventext(newtoken,_P_nextchr,tmp);
K_settype(newtoken,K_FLO_NUM);
if (!(_P_flags & U_BYTE_COMPARE))
{
K_setfloat(newtoken,
F_atof(K_gettext(newtoken),
USE_ALL));
/*
** assign the curent tolerance
*/
K_settol(newtoken,T_gettol(_P_next_tol));
}
/*
** use next tolerance in the
** specification if there is one
*/
if (T_moretols(_P_next_tol))
{
_P_next_tol++;
}
/*
** and move pointer past the float
*/
_P_nextchr += tmp;
}
/*
** is this a fixed point number
*/
else if (isdigit(*_P_nextchr))
{
for(ptr=_P_nextchr; isdigit(*ptr); ptr++)
{
}
K_saventext(newtoken,_P_nextchr,ptr-_P_nextchr);
K_settype(newtoken,K_LIT);
_P_nextchr = ptr;
}
/*
** try an alpha-numeric word
*/
else if (isalpha(*_P_nextchr) || _P_in_alpha(*_P_nextchr))
{
/*
** it's a multi character word
*/
for(ptr = _P_nextchr;
isalpha(*ptr)
|| isdigit(*ptr)
|| _P_in_alpha(*ptr);
ptr++)
{
}
K_saventext(newtoken,_P_nextchr,ptr-_P_nextchr);
K_settype(newtoken,K_LIT);
_P_nextchr = ptr;
}
else
{
/*
** otherwise, treat the char itself as a token
*/
K_saventext(newtoken,_P_nextchr,1);
K_settype(newtoken,K_LIT);
_P_nextchr++;
}
K_settoken(_P_fnumb,K_gettmax(_P_fnumb),newtoken);
L_inccount(_P_fnumb,L_gettlmax(_P_fnumb));
/*
** if we are out of space, complain and quit
*/
if (K_inctmax(_P_fnumb))
{
(void) sprintf(Z_err_buf,
"warning -- to many tokens in file only first %d tokens will be used.\n",
K_MAXTOKENS);
Z_complain(Z_err_buf);
return;
}
#ifndef NOCHATTER
if (0 == (K_gettmax(_P_fnumb) % _P_PARSE_CHATTER))
{
int max = K_gettmax(_P_fnumb);
(void) sprintf(Z_err_buf,
"scanned %d words from file #%d\n",
max,_P_fnumb+1);
Z_chatter(Z_err_buf);
}
#endif
/*
** are we done?
*/
if(ret_code)
{
return;
}
} /* loop once per object on a line */
#ifndef lint
Z_fatal("this line should never execute");
#endif
}
void
P_file_parse(num,strt,lcnt,flags)
int num; /* file number */
int strt; /* first line to parse expressed in real line numbers */
int lcnt; /* max number of lines to parse */
int flags; /* flags for controlling the parse mode */
{
/*
** set module-wide state variables
*/
_P_fnumb = num;
_P_start = strt;
_P_lcount = lcnt;
_P_flags = flags;
_P_initparser();
_P_nextchr = _P_dummyline;
_P_has_content = 0;
_P_next_tol = 0;
L_setcount(_P_fnumb,L_gettlmax(_P_fnumb),0);
/*
** start everything back one line (it will be incremented
** just before the first line is accessed
*/
_P_realline = _P_start-1;
_P_do_parse();
/*
** if the last line had content, increment the count
*/
if (_P_has_content)
{
/*
** this code will get executed if we stopped parsing in the middle
** of a line. i haven't looked at this case carefully.
** so, there is a good chance that it is buggy.
*/
(void) sprintf(Z_err_buf,"parser got confused at end of file\n");
Z_complain(Z_err_buf);
L_incclmax(_P_fnumb);
if (L_getcount(_P_fnumb,L_gettlmax(_P_fnumb)))
L_inctlmax(_P_fnumb);
}
return;
}

10
utils/Spiff/parse.h Normal file
View File

@ -0,0 +1,10 @@
/* 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.
*/
extern void P_file_parse();
extern void P_addalpha();

454
utils/Spiff/spiff.1 Normal file
View File

@ -0,0 +1,454 @@
.ll 6i
.pl 10.5i
.po 1.25i
.\" @(#)spiff.1 1.0 (Bellcore) 9/20/87
.\"
.lt 6.0i
.TH SPIFF 1 "February 2, 1988"
.AT 3
.SH NAME
spiff \- make controlled approximate comparisons between files
.SH SYNOPSIS
.B spiff
[
.B \-s
script ] [
.B \-f
sfile ] [
.B \-bteviqcdwm
] [
.B \-a
\(br
.B \-r
value ] \-value file1 file2
.SH DESCRIPTION
.I Spiff
compares the contents of
.B file1
and
.B file2
and prints a description of the important differences between
the files.
White space is ignored except to separate other objects.
.I Spiff
maintains tolerances below which differences between two floating point
numbers are ignored.
Differences in floating point notation (such as 3.4 3.40 and 3.4e01)
are treated as unimportant.
User specified delimited strings (i.e. comments) can also be ignored.
Inside other user specified delimited strings
(i.e. quoted strings) whitespace can be significant.
.PP
.I Spiff's
operation can be altered via command line options, a command script, and with
commands that are embedded in the input files.
.PP
The following options affect
.I spiff's
overall operation.
.TP
.B \-q
suppresses warning messages.
.TP
.B \-v
use a visually oriented display. Works only in MGR windows.
.PP
.I Spiff
has several flags to aid differencing of various programming languages.
See EXAMPLES for a detailed description of the effects of these flags.
.TP
.B \-C
treat the input files as C program source code.
.TP
.B \-S
treat the input files as Bourne shell program source code.
.TP
.B \-F
treat the input files as Fortran program source code.
.TP
.B \-M
treat the input files as Modula-2 program source code.
.TP
.B \-L
treat the input files as Lisp program source code.
.PP
By default, the output looks somewhat similar in appearance
to the output of diff(1). Lines with differences are printed with
the differences highlighted. If stdout is a terminal, as determined
by isatty(), then highlighting uses standout mode as determined by termcap.
If stdout is not a tty, then the underlining (via underscore/backspace/char)
is used to highlight differences.
The following option can control the format of the ouput.
.TP
.B \-t
produce output in terms of individual tokens. This option is
most useful for debugging as the output produced is verbose to
the point of being unreadable.
.PP
The following option controls the differencing algorithm.
.TP
.B \-e
compare each token
in the files with the object in the same ordinal
position in the other file. If the files have a different number
of objects, a warning message is printed
and the objects at the end of the longer file are ignored.
By default,
.I spiff
uses a Miller/Myers algorithm to find a minimal edit sequence
that will convert the contents of the first file into the second.
.TP
\-<decimal-value>
sets a limit on the total number of insertions and deletions
that will be considered.
If the files differ by more than the stated amount,
the program will give up, print a warning message, and exit.
.PP
The following options control the command script. More than one of
each may appear at at time. The commands accumulate.
.TP
.B \-f sfile
a command script to be taken from file
.IR sfile
.TP
.B \-s command-script
causes the following argument to be taken as a command script.
.PP
The following options control how individual objects are compared.
.TP
.B \-b
treat all objects (including floating point numbers) as literals.
.TP
.B \-c
ignore differences between upper and lower case.
.PP
The following commands will control how the files are parsed.
.TP
.B \-w
treat white space as objects. Each white space character will
be treated as a separate object when the program is comparing the
files.
.TP
.B \-m
treat leading sign characters ( + and \- ) as separate even
if they are followed by floating point numbers.
.TP
.B \-d
treat integer decimal numbers (such as 1987) as real numbers (subject to
tolerances) rather than as literal strings.
.PP
The following three flags are used to set the default tolerances.
The floating-point-numbers may be given in the formats accepted
by atof(3).
.TP
.B \-a floating-point-number
specifies an absolute value for the tolerance in floating point numbers.
The flag
.B \-a1e-2
will cause all differences greater than 0.01 to be reported.
.TP
.B \-r floating-point-number
specifies a relative tolerance. The value given is interpreted
as a fraction of the larger (in absolute terms)
of the two floating point numbers being compared.
Thus, the flag
.B \-r0.1
will cause the two floating point numbers 1.0 and 0.9 to be deemed within
tolerance. The numbers 1.0 and 0.89 will be outside the tolerance.
.TP
.B \-i
causes differences between floating point numbers to be ignored.
.PP
If more than one
.B \-a, \-r,
or
.B \-i
flag appear on the command line,
the tolerances will be OR'd together (i.e. any difference that is within
any of the tolerances will be ignored).
.PP
If no default tolerances is set on the command line,
the program will use a default tolerance of
.B '\-a 1e-10 \-r 1e-10'.
.SH SCRIPT COMMANDS
.PP
A script consists of commands, one per line.
Each command consists of a keyword possibly followed by arguments.
Arguments are separated by one or more tabs or spaces.
The commands are:
.TP
literal BEGIN-STRING [END-STRING [ESCAPE-STRING]]
Specifies the delimiters surrounding text that is to be treated as a single
literal object. If only one argument is present, then only that string itself is treated
as a literal. If only two arguments are present, they are taking as the starting
and ending delimiters respectively. If three arguments are present, they are treated
as the start delimiter, end delimiter, and a string that may be used to escape
an instance of the end delimiter.
.TP
beginchar BEGINNING-OF-LINE-CHARACTER
Set the the beginning of line character for BEGIN-STRING's in comments.
The default is '^'.
.TP
endchar END-OF-LINE-CHARACTER
Set the end of line character for END-STRING's in comments.
The default is '$'.
.TP
addalpha NEW-ALPHA-CHARACTER
Add NEW-ALPHA-CHARACTER to the set of characters allowed in literal strings.
By default,
.I spiff
parses sequences of characters that begin with a letter and followed by
zero or more letters or numbers as a single literal token. This definition
is overly restrictive when dealing with programming languages.
For example, in the C programming language,
the underscore character is allowed in identifiers.
.TP
comment BEGIN-STRING [END-STRING [ESCAPE-STRING]]
Specifies the delimiters surrounding text that is to be be ignored entirely
(i.e. viewed as comments).
The operation of the comment command is very similar to the literal command.
In addition, if the END-STRING consists of only
the end of line character, the end of line will delimit the end of the comment.
Also, if the BEGIN-STRING starts with the beginning of line character, only
lines that begin with the BEGIN-STRING will be ignored.
.PP
More than one comment specification and more than one literal string specification
may be specified at a time.
.TP
nestcom BEGIN-STRING [END-STRING [ESCAPE-STRING]]
Similar to the comment command, but allows comments to be nested.
Note, due to the design of the parser nested comments can not
have a BEGIN-STRING that starts with the beginning of line character.
.TP
resetcomments
Clears the list of comment specifications.
.TP
resetliterals
Clears the list of literal specifications.
.TP
tol [aVALUE\(brrVALUE\(bri\(brd . . . [ ; aVALUE\(brrVALUE\(bri\(brd . . . ] . . . ]
set the tolerance for floating point comparisons.
The arguments to the tol command are a set of tolerance specifications
separated by semicolons. If more than one a,r,d, or i appears within
a specification, then the tolerances are OR'd together (i.e. any difference
that is within any tolerance will be ignored).
The semantics of a,r, and i are identical to the
.B \-a, \-r,
and
.B \-i
flags. The d means that the default tolerance (as specified by the invocation
options) should be used.
If more than one specification appears on the line, the first
specification is applied to the first floating point number on each line,
the second specification to the second floating point number one each line
of the input files, and so on. If there are more floating point numbers
on a given line of input than tolerance specifications,
the last specification is used repeatedly for all remaining floating point numbers
on that line.
.TP
command STRING
lines in the input file that start with STRING will be interpreted as
command lines. If no "command" is given as part of a
.B \-s
or
.B \-f
then it will be impossible to embed commands in the input files.
.TP
rem
.TP
#
used to places human readable remarks into a commands script. Note that the
use of the '#' character differs from other command languages (for instance
the Bourne shell).
.I Spiff
will only recognize the '#' as beginning a comment when it is the first
non-blank character on the command line. A '#' character appearing elsewhere
will be treated as part of the command. Cautious users should use 'rem'.
Those hopelessly addicted to '#' as a comment character can have command
scripts with a familiar format.
.PP
Tolerances specified in the command scripts have precedence over the tolerance
specified on the invocation command line. The tolerance specified in
.I file1
has precedence over the tolerance specified in
.I file2.
.PP
.SH VISUAL MODE
If
.I spiff
is invoked with the \-v option, it will enter an interactive mode rather
than produce an edit sequence. Three windows will be put on the screen.
Two windows will contain corresponding segments of the input files.
Objects that appear in both segments will be examined for differences and
if any difference is found, the objects will be highlighted in reverse video
on the screen. Objects that appear in only one window will have a line drawn
through them to indicate that they aren't being compared with anything in the other
text window. The third window is a command window. The command window will
accept a single tolerance specification (followed by a newline)
in a form suitable to the
.B tol
command. The tolerance specified will then be used as the default tolerance
and the display will be updated to highlight only those objects that exceed
the new default tolerance. Typing
.B m
(followed by a newline) will display the next screenfull of text. Typing
.B q
(followed by a newline) will cause the program to exit.
.SH LIMITS
Each input files can be no longer that 10,000 line long or contain more
than 50,000 tokens. Longer files will be truncated.
No line can be longer than 1024 characters. Newlines
will be inserted every 1024 character.
.SH EXAMPLES
.TP
spiff \-e \-d foo bar
this invocation (using exact match algorithm and treating integer numbers
as if they were floats) is very useful for examining large tables of numbers.
.TP
spiff \-0 foo bar
compare the two files, quitting after the first difference is found.
This makes the program operate roughly like cmp(1).
.TP
spiff \-0 -q foo bar
same as the above, but no output is produced.
The return code is still useful.
.TP
spiff \-w \-b foo bar
will make the program operate much like diff(1).
.TP
spiff \-a1e-5 \-r0.001 foo bar
compare the contents of the files foo and bar and ignore all differences between
floating point numbers that are less than or equal to
0.00001 or 0.1% of the number of larger magnitude.
.TP
tol a.01 r.01
will cause all differences between floating point numbers that are less than
or equal to
0.01 or 1% of the number of larger magnitude to be ignored.
.TP
tol a.01 r.01 ; i
will cause the tolerance in the previous example to be applied to the first
floating point number on each line. All differences between the second and
subsequent floating point numbers on each line will be ignored.
.TP
tol a.01 r.01 ; i ; a.0001
like the above except that only differences between the second floating point
number on each line will be ignored. The differences between
third and subsequent floating point numbers on each number will be ignored if they
are less than or equal to 0.0001.
.IP
A useful script for examing C code is:
.nf
literal " " \\
comment /* */
literal &&
literal \(br\(br
literal <=
literal >=
literal !=
literal ==
literal --
literal ++
literal <<
literal >>
literal ->
addalpha _
tol a0
.fi
.IP
A useful script for shell programs is:
.nf
literal ' ' \\
comment # $
tol a0
.fi
.IP
A useful script for Fortran programs is:
.nf
literal ' ' '
comment ^C $
tol a0
.fi
.IP
A useful script for Modula 2 programs is:
.nf
literal ' '
literal " "
nestcom (* *)
literal :=
literal <>
literal <=
literal >=
tol a0
.fi
.IP
A useful script for Lisp programs is:
.nf
literal " "
comment ; $
tol a0
.fi
.SH DIAGNOSTICS
.I Spiff's
exit status is 0 if no differences are found, 1 if differences are found, and
2 upon error.
.SH BUGS
In C code, escaped newlines will appear as differences.
.PP
Comments are treated as token delimiters.
.PP
Comments in Basic don't work right. The line number is not ignored.
.PP
Continuation lines in Fortran comments don't work.
.PP
There is no way to represent strings specified using a
Hollerith notation in Fortran.
.PP
In formated English text, hyphenated words,
movements in pictures, footnotes, etc.
will be reported as differences.
.PP
STRING's in script commands can not include whitespace.
.PP
Visual mode does not handle tabs properly. Files containing
tabs should be run through
expand(1) before trying to display them with visual mode.
.PP
In visual mode, the text windows appear in a fixed size and font.
Lines longer than the window size will not be handled properly.
.PP
Objects (literal strings) that contain newlines cause trouble in several places
in visual mode.
.PP
Visual mode should accept more than one tolerance specification.
.PP
When using visual mode or the exact match comparison algorithm, the program
should do the parsing on the fly rather than truncating long files.
.SH AUTHOR
Daniel Nachbar
.SH COPYRIGHT
.nf
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.
.fi
.br
.SH SEE ALSO
atof(3)
isatty(2)
diff(1)
cmp(1)
expand(1)
mgr(1L)
.PP
"Spiff -- A Program for Making Controlled Approximate Comparisons of Files",
by Daniel Nachbar.
.PP
"A File Comparison Program" by Webb Miller and Eugene W. Myers in Software \-
Practice and Experience, Volume 15(11), pp.1025-1040, (November 1985).

341
utils/Spiff/spiff.c Normal file
View File

@ -0,0 +1,341 @@
/* 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$";
#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;
/*
** 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];
}

162
utils/Spiff/strings.c Normal file
View File

@ -0,0 +1,162 @@
/* 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$";
#endif
#include <ctype.h>
#include "misc.h"
#include "strings.h"
/*
** routines for handling strings.
** several routines manipulate "words"
** a "word" is a string not containing whitespace
*/
/*
** copy a single word. similar to strcpy
*/
void
S_wordcpy(to,from)
char *to, *from;
{
while ((*from != '\0') && isprint(*from) && (!isspace(*from)))
{
*to++ = *from++;
}
*to = '\0';
return;
}
/*
** find the next whitespace character. The address of the pointer
** is passed and the pointer itself is changed.
*/
void
S_skipword(theptr)
char **theptr;
{
while((**theptr != '\0') && isprint(**theptr) && (!isspace(**theptr)))
{
(*theptr)++; /* increment the pointer, NOT the pointer
to the pointer */
}
return;
}
/*
** find the next non-whitespace character. The address of the pointer
** is passed and the pointer itself is changed.
*/
void
S_skipspace(theptr)
char **theptr;
{
while((**theptr != '\0') && isspace(**theptr))
{
(*theptr)++; /* increment the pointer, NOT the pointer
to the pointer */
}
return;
}
/*
** move the pointer to the beginning of the next word
*/
void
S_nextword(theptr)
char **theptr;
{
S_skipword(theptr);
S_skipspace(theptr);
return;
}
/*
** see if the first string is a prefix of the second
** returns 0 if yes
** non zero if now
** sigh -- the way strcmp does
*/
int
S_wordcmp(s1,s2)
char *s1,*s2;
{
return(strncmp(s1,s2,strlen(s2)));
}
/*
** chop off any trailing zeros on a string
** but leave one zero if there are only zeros
*/
void
S_trimzeros(str)
char *str;
{
/*
** end starts out pointing at the null terminator
*/
char *end = str + strlen(str);
/*
** if there is more than one character left in the string
*/
while(end > (str+1))
{
--end;
if ('0' == *end)
{
*end = '\0';
}
else
{
return;
}
}
return;
}
/*
** save a copy of the string
*/
void
S_savestr(to,from)
char **to,*from;
{
S_allocstr(to,strlen(from));
(void) strcpy(*to,from);
return;
}
/*
** save cnt characters of the string
*/
void
S_savenstr(to,from,cnt)
char **to,*from;
{
S_allocstr(to,cnt);
(void) strncpy(*to,from,cnt);
*((*to)+cnt) = '\0';
return;
}
/*
** allocate space for a string, add 1 to size to
** make sure that there is room for the terminating null character
*/
void
S_allocstr(to,size)
char **to;
int size;
{
*to = Z_ALLOC(size+1,char);
}

20
utils/Spiff/strings.h Normal file
View File

@ -0,0 +1,20 @@
/* 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 S_INCLUDED
extern void S_wordcpy();
extern void S_skipword();
extern void S_skipspace();
extern void S_nextword();
extern int S_wordcmp();
extern void S_trimzeros();
extern void S_savestr();
extern void S_savenstr();
extern void S_allocstr();
#define S_INCLUDED
#endif

37
utils/Spiff/token.c Normal file
View File

@ -0,0 +1,37 @@
/* 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$";
#endif
#include "misc.h"
#include "token.h"
K_token _K_ato[K_MAXTOKENS]; /* storage for tokens */
K_token _K_bto[K_MAXTOKENS];
int _K_atm;
int _K_btm;
void
K_settoken(file,index,pointer)
int file;
int index;
K_token pointer;
{
if (file)
{
_K_bto[index] = pointer;
}
else
{
_K_ato[index] = pointer;
}
}

81
utils/Spiff/token.h Normal file
View File

@ -0,0 +1,81 @@
/* 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 K_INCLUDED
#include "float.h"
#include "tol.h"
#include "strings.h"
#define K_MAXTOKENS 50000
/*
** values for token type
*/
#define K_LIT 1
#define K_FLO_NUM 2
typedef struct {
int linenum; /* line that the token started on */
int pos; /* position on the line where token started */
int type; /* token type */
char *text; /* literal token text */
/*
** canonical floationg point representation
*/
F_float flo_num;
T_tol tolerance;
} _K_str, *K_token;
/*
** this should really be a two dimensional array
** but i'm too lazy to recode it
*/
extern K_token _K_ato[]; /* storage for the tokens */
extern K_token _K_bto[];
/*
** save token X from file
*/
extern void K_settoken(/*file,X,ptr*/);
#define K_gettoken(file, X) (file?(_K_bto[X]):(_K_ato[X]))
extern int _K_atm; /* count of tokens */
extern int _K_btm;
/*
** get token number X from file
*/
#define K_get_token(file, X) (file?(_K_bto[X]):(_K_ato[X]))
#define K_gettmax(file) (file?_K_btm:_K_atm)
#define K_settmax(file,value) (file?(_K_btm=(value)):(_K_atm=(value)))
/*
** increment and return true on overflow
*/
#define K_inctmax(file) ((file?(++_K_btm):(++_K_atm))>=K_MAXTOKENS)
#define K_setline(x,y) (x->linenum = y)
#define K_setpos(x,y) (x->pos = y)
#define K_settext(x,y) (x->text = y)
#define K_savetext(x,y,z) S_savestr(&(x->text),y)
#define K_saventext(x,y,z) S_savenstr(&(x->text),y,z)
#define K_setfloat(x,y) (x->flo_num = y)
#define K_settol(x,y) (x->tolerance = y)
#define K_settype(x,y) (x->type = y)
#define K_getline(x) (x->linenum)
#define K_getpos(x) (x->pos)
#define K_gettext(x) (x->text)
#define K_getfloat(x) (x->flo_num)
#define K_gettol(x) (x->tolerance)
#define K_gettype(x) (x->type)
#define K_maketoken() (Z_ALLOC(1,_K_str))
#define K_INCLUDED
#endif

361
utils/Spiff/tol.c Normal file
View File

@ -0,0 +1,361 @@
/* 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$";
#endif
#include "misc.h"
#include "float.h"
#include "tol.h"
#include "token.h"
/*
** storage for the default tolerances
*/
T_tol _T_gtol = _T_null;
/*
** tolerances that can be set in the command script and attached to floating
** point numbers at parse time
*/
static T_tol _T_tols[_T_TOLMAX];
/*
** initialize the global tolerance
** should be called only once at the beginning of the program
*/
void
T_initdefault()
{
static int called_before = 0;
if (called_before)
{
Z_fatal("T_initdefault called more than once\n");
}
/*
** if the default tolerance was set somewhere else
** don't set it here
*/
if (T_isnull(_T_gtol))
{
T_defatol(_T_ADEF);
T_defrtol(_T_RDEF);
}
called_before = 1;
}
static void
_T_tolclear(addr)
T_tol *addr;
{
*addr = _T_null;
}
/*
** clear the parse time tolerances
*/
void
T_clear_tols()
{
int i;
for(i=0;i<_T_TOLMAX;i++)
{
_T_tolclear(&_T_tols[i]);
}
}
static void
_T_defclear()
{
_T_tolclear(&_T_gtol);
}
/*
** take a series of specifiers and add them to the tolerance
*/
static void
_T_settol(toladdr,str)
T_tol *toladdr;
char *str;
{
char typechar;
while ('\0' != *str)
{
/*
** find the first non-whitespace character
*/
S_skipspace(&str);
/*
** snarf up the type specifier
*/
typechar = *str;
/*
** now skip the first char
*/
str++;
/*
** skip any possibly intervening whitespace
*/
S_skipspace(&str);
switch (typechar)
{
case 'a':
_T_addtol(toladdr,T_ABSOLUTE,str);
break;
case 'r':
_T_addtol(toladdr,T_RELATIVE,str);
break;
case 'i':
_T_addtol(toladdr,T_IGNORE,(char*)0);
break;
case 'd':
_T_appendtols(toladdr,_T_gtol);
break;
default:
(void) sprintf(Z_err_buf,
"don't understand tolerance type '%c'\n",typechar);
Z_fatal(Z_err_buf);
}
/*
** and skip to next tolerance
*/
S_nextword(&str);
}
}
/*
** set the default tolerance
*/
void
T_setdef(str)
char *str;
{
_T_defclear();
_T_settol(&_T_gtol,str);
}
static char*
_T_nextspec(ptr)
char *ptr;
{
/*
** find the end of the current spec
*/
for(;(_T_SEPCHAR != *ptr) && ('\0' != *ptr);ptr++)
{
}
/*
** and step over the seperator if necessary
*/
if (_T_SEPCHAR == *ptr)
ptr++;
return(ptr);
}
/*
** return just the next set of specs
** ie the string up to end of line or
** the first _T_SEPCHAR
** returned string does not include the _T_SEPCHAR
*/
static char *
_T_getspec(from)
char *from;
{
static char retval[Z_LINELEN];
char *ptr = retval;
while((_T_SEPCHAR != *from) && ('\0' != *from))
{
*ptr++ = *from++;
}
*ptr = '\0'; /* terminate the line */
return(retval);
}
/*
** parse a series of _T_SEPCHAR separated tolerance specifications
*/
void
T_tolline(str)
char *str;
{
int nexttol;
T_clear_tols();
for(nexttol=0;'\0' != *str;nexttol++,str = _T_nextspec(str))
{
/*
** make sure we haven't run off the end
*/
if (nexttol >= _T_TOLMAX)
{
Z_fatal("too many tolerances per line");
}
/*
** and set the tolerance
*/
_T_settol(&_T_tols[nexttol],_T_getspec(str));
}
}
T_moretols(next_tol)
{
return((next_tol >= 0) &&
(_T_TOLMAX-1 > next_tol) &&
(!T_isnull( _T_tols[next_tol+1])));
}
T_tol
T_gettol(index)
int index;
{
return(_T_tols[index]);
}
/*
** chose which tolerance to use
** precidence is
** first tolerance
** second tolerance
** default tolerance
*/
T_tol
T_picktol(p1,p2)
T_tol p1, p2;
{
if (!(T_isnull(p1)))
return(p1);
if (!(T_isnull(p2)))
return(p2);
return(_T_gtol);
}
void
_T_appendtols(to,from)
T_tol *to,from;
{
T_tol last;
/*
** are there any elements on the list yet
*/
if (T_isnull(*to))
{
/*
** it's a null list, so allocat space for the
** first element and set pointer to it.
*/
*to = from;
}
else
{
/*
** find the last element on the list
*/
for(last= *to;!T_isnull(T_getnext(last));last = T_getnext(last))
{
}
/*
** add an element on the end
*/
T_setnext(last,from);
}
}
/*
** add a tolerance to a list
*/
void
_T_addtol(listptr,type,str)
T_tol *listptr;
int type;
char *str;
{
T_tol last;
/*
** are there any elements on the list yet
*/
if (T_isnull(*listptr))
{
/*
** it's a null list, so allocat space for the
** first element and set pointer to it.
*/
last = *listptr = Z_ALLOC(1,_T_struct);
}
else
{
/*
** find the last element on the list
*/
for(last= *listptr;!T_isnull(T_getnext(last));last = T_getnext(last))
{
}
/*
** add an element on the end
*/
T_setnext(last,Z_ALLOC(1,_T_struct));
/*
** and point to the new element
*/
last = T_getnext(last);
}
T_settype(last,type);
T_setnext(last,_T_null);
/*
** set the float value only if necessary
*/
if (T_IGNORE == type)
{
T_setfloat(last,F_null);
}
else
{
T_setfloat(last,F_atof(str,NO_USE_ALL));
/*
** test new tolerance for sanity
*/
if (F_getsign(T_getfloat(last)))
{
(void) sprintf(Z_err_buf,
"%s : negative tolerances don't make any sense\n",str);
Z_fatal(Z_err_buf);
}
/*
** check for excessively large relative tolerances
*/
if ((T_RELATIVE == type) &&
(F_floatcmp(T_getfloat(last),
F_atof("2.0",USE_ALL)) > 0))
{
(void) sprintf(Z_err_buf,
"%s : relative tolerances greater than 2 don't make any sense\n",str);
Z_fatal(Z_err_buf);
}
}
}

64
utils/Spiff/tol.h Normal file
View File

@ -0,0 +1,64 @@
/* 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.
*/
#include "float.h"
#ifndef T_INCLUDED
/*
** values for tol_type
*/
#define T_ABSOLUTE 0
#define T_RELATIVE 1
#define T_IGNORE 2
typedef struct _T_tstr{
int tol_type; /* one of the above */
F_float flo_tol; /* tolerance is expressed in
terms of a floating point value */
struct _T_tstr *next;
} _T_struct, *T_tol;
#define _T_TOLMAX 10 /* number of tolerances that can
be in effect at one time */
#define _T_ADEF "1e-10" /* default absolute tolerance */
#define _T_RDEF "1e-10" /* default relative tolerance */
extern T_tol T_gettol();
extern void T_clear_tols();
extern void T_initdefault();
extern void T_setdef();
extern void T_tolline();
extern T_tol T_picktol();
#define T_gettype(x) (x->tol_type)
#define T_getfloat(x) (x->flo_tol)
#define T_getnext(x) (x->next)
#define T_settype(x,y) (x->tol_type = y)
#define T_setfloat(x,y) (x->flo_tol = y)
#define T_setnext(x,y) (x->next = y)
#define _T_null ((T_tol) 0)
#define T_isnull(x) ((x) == _T_null)
extern T_tol _T_gtol;
extern void _T_addtol();
extern void _T_appendtols();
/*
** routines for building the global tolerance list
*/
#define T_defatol(x) _T_addtol(&_T_gtol,T_ABSOLUTE,x)
#define T_defrtol(x) _T_addtol(&_T_gtol,T_RELATIVE,x)
#define T_defitol() _T_addtol(&_T_gtol,T_IGNORE,(char*)NULL)
#define _T_SEPCHAR ';'
#define T_INCLUDED
#endif

411
utils/Spiff/visual.c Normal file
View File

@ -0,0 +1,411 @@
/* 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$";
#endif
#ifdef MGR
#include "misc.h"
#include "line.h"
#include "token.h"
#include "/usr/public/pkg/mgr/include/term.h"
#include "/usr/public/pkg/mgr/include/restart.h"
#define OTHER 0
#define ON_DEBUG 1
#define OFF_DEBUG 2
#define DO_QUIT 3
#define DO_PAGE 4
#define NEW_PREC 5
#define NROW 60
#define NCOL 80
int isdiff[MAXTOKENS]; /* flag showing if a token pair was shown different*/
int comwin,wina, winb; /* location to store window numbers */
int fontx,fonty; /* size of the font in pixels */
int debug =0;
int firsttoken = 0; /* index of first token pair being displayed */
int tokencnt; /* count of the number of token pairs being displayed */
V_visual(flags)
int flags;
{
int moretodo = 1; /* flag to clear when we're finished */
messup();
m_selectwin(comwin);
m_setmode(W_ACTIVATE);
showpages(comroutine,flags);
do
{
switch(getinput())
{
case ON_DEBUG:
debug = 0;
break;
case OFF_DEBUG:
debug = 1;
break;
case DO_QUIT:
moretodo = 0;
break;
case DO_PAGE:
if((firsttoken+tokencnt>= K_gettmax(0))||
(firsttoken+tokencnt>= K_gettmax(1)))
{
m_selectwin(comwin);
m_printstr("\007this is the last page\n");
break;
}
firsttoken += tokencnt;
showpages(comroutine,flags);
break;
case NEW_PREC:
updatepages(comroutine,flags);
break;
case OTHER:
break;
default :
Z_fatal("bad value in main switch");
}
} while (moretodo);
V_cleanup();
return(0);
}
getinput()
{
char ibuf[Z_LINELEN]; /* input buffer */
char *ptr;
m_selectwin(comwin);
m_setmode(W_ACTIVATE);
switch (m_getchar())
{
case 't':
m_gets(ibuf);
/*
** skip the 'tol'
*/
ptr = ibuf;
S_nextword(&ptr);
T_setdef(ptr);
return(NEW_PREC);
case 'q':
return(DO_QUIT);
case 'd':
return(OFF_DEBUG);
case 'D':
return(ON_DEBUG);
case 'm':
return(DO_PAGE);
default:
return(OTHER);
}
}
showpages(comroutine,flags)
int (*comroutine)();
int flags;
{
int i;
m_selectwin(wina);
m_clear();
m_selectwin(winb);
m_clear();
showlines();
for(i=firsttoken;i<tokencnt+firsttoken; i++)
{
isdiff[i] = 0;
}
updatepages(comroutine,flags);
}
updatepages(comroutine,flags)
int (*comroutine)();
int flags;
{
int i;
for(i=firsttoken;i<tokencnt+firsttoken; i++)
{
if (isdiff[i])
{
if (0 == X_com(i,i,flags))
{
m_selectwin(wina);
un_highlight(0,K_gettoken(0,i),K_getline(K_gettoken(0,firsttoken)));
m_selectwin(winb);
un_highlight(1,K_gettoken(1,i),K_getline(K_gettoken(1,firsttoken)));
isdiff[i] = 0;
}
}
else
{
if (0 != X_com(i,i,flags))
{
m_selectwin(wina);
highlight(0,K_gettoken(0,i),K_getline(K_gettoken(0,firsttoken)));
m_selectwin(winb);
highlight(1,K_gettoken(1,i),K_getline(K_gettoken(1,firsttoken)));
isdiff[i] = 1;
}
}
}
}
un_highlight(file,ptr,firstline)
int file;
K_token ptr;
int firstline;
{
highlight(file,ptr,firstline);
}
/*
** argument expressed in terms of token lines
*/
highlight(file,ptr,firstline)
int file;
K_token ptr;
int firstline;
{
int startx = K_getpos(ptr)*fontx;
int starty = (L_tl2cl(file,K_getline(ptr))-L_tl2cl(file,firstline))*fonty;
int sizex = fontx*strlen(K_gettext(ptr));
int sizey = fonty;
m_bitwrite(startx,starty,sizex,sizey);
}
showlines()
{
int Alinecnt = 0;
int Blinecnt = 0;
int Atfirstline = K_getline(K_gettoken(0,firsttoken));
int Btfirstline = K_getline(K_gettoken(1,firsttoken));
int Afirstline = L_tl2cl(0,K_getline(K_gettoken(0,firsttoken)));
int Bfirstline = L_tl2cl(1,K_getline(K_gettoken(1,firsttoken)));
int Anexttoken = L_getindex(0,Atfirstline);
int Bnexttoken = L_getindex(1,Btfirstline);
int i;
/*
** first print the lines on the screen
*/
for(i=0;i < NROW;i++)
{
if(Afirstline+i < L_getclmax(0))
{
m_selectwin(wina);
showline(0,Afirstline+i,i);
Alinecnt++;
}
if(Bfirstline+i < L_getclmax(1))
{
m_selectwin(winb);
showline(1,Bfirstline+i,i);
Blinecnt++;
}
}
/*
** now figure out how many tokens we actually printed
*/
for(i=Atfirstline;Anexttoken<K_gettmax(0) && L_tl2cl(0,i) < Afirstline+Alinecnt;i++)
{
Anexttoken += L_getcount(0,i);
}
for(i=Btfirstline;Bnexttoken<K_gettmax(1) && L_tl2cl(1,i) < Bfirstline+Blinecnt;i++)
{
Bnexttoken += L_getcount(1,i);
}
tokencnt = MIN(Anexttoken,Bnexttoken) - firsttoken;
/*
** draw a line through any tokens that come before the first
** token that is being compared
*/
if (L_getindex(0,Atfirstline) != firsttoken)
{
m_selectwin(wina);
for(i=L_getindex(0,Atfirstline);i<firsttoken;i++)
{
drawline(K_gettoken(0,i),0);
}
}
if (L_getindex(1,Btfirstline) != firsttoken)
{
m_selectwin(winb);
for(i=L_getindex(1,Btfirstline);i<firsttoken;i++)
{
drawline(K_gettoken(1,i),0);
}
/*
m_line(Bt[Bindex[Bfirstline]]->pos*fontx,fonty/2,(Bt[firsttoken]->pos*fontx)-2,fonty/2);
*/
}
if (Anexttoken > Bnexttoken)
{
m_selectwin(wina);
for(i=Bnexttoken;i<Anexttoken;i++)
{
drawline(K_gettoken(0,i),L_tl2cl(0,K_getline(K_gettoken(0,i)))-Afirstline);
}
}
if (Anexttoken < Bnexttoken)
{
m_selectwin(winb);
for(i=Anexttoken;i<Bnexttoken;i++)
{
drawline(K_gettoken(1,i),L_tl2cl(1,K_getline(K_gettoken(1,i)))-Bfirstline);
}
}
}
/*
** line is given in conten line
*/
drawline(ptr,line)
K_token ptr;
int line;
{
m_line(K_getpos(ptr)*fontx,
(line*fonty)+fonty/2,
(K_getpos(ptr)+strlen(K_gettext(ptr)))*fontx,
(line*fonty)+fonty/2);
}
/*
** takes arguments in terms of content lines
*/
showline(file,index,row)
int file;
int index;
int row;
{
static char tmp[Z_LINELEN];
m_move(0,row);
stripnl(tmp,L_getcline(file,index));
m_printstr(tmp);
}
stripnl(to,from)
char *to,*from;
{
while ((*from != '\n') && (*from != '\0'))
{
*to++ = *from++;
}
*to = '\0';
}
static int didscr = 0;
messup()
{
int col, row;
int dum1,dum2,dum3,border;
m_setup(W_FLUSH|W_DEBUG);
m_push(P_EVENT|P_FLAGS|P_POSITION);
get_param(&dum1,&dum2,&dum3,&border);
didscr = 1;
comwin = m_makewindow(192,50,732,116);
wina = m_makewindow(0,218,570,670);
m_selectwin(wina);
m_font(2);
get_font(&fontx,&fonty);
m_shapewindow(0,218,NCOL*fontx+(2*border),NROW*fonty+(2*border));
get_colrow(&col,&row);
if ((col != NCOL) || (row != NROW))
{
Z_fatal("bad window size");
}
m_func(B_INVERT);
m_setmode(W_ABS);
winb = m_makewindow(580,218,570,670);
m_selectwin(winb);
m_font(2);
get_font(&fontx,&fonty);
m_shapewindow(580,218,NCOL*fontx+(2*border),NROW*fonty+(2*border));
get_colrow(&col,&row);
if ((col != NCOL) || (row != NROW))
{
Z_fatal("bad window size");
}
m_func(B_INVERT);
m_setmode(W_ABS);
m_selectwin(comwin);
m_clear();
m_setmode(W_ABS);
m_setmode(W_ACTIVATE);
}
V_cleanup()
{
if (didscr)
{
m_destroywin(wina);
m_destroywin(winb);
m_destroywin(comwin);
m_popall();
m_setecho();
(void) fclose(m_termin);
(void) fclose(m_termout);
}
}
#else
#include "misc.h"
/*
** dummy code for systems that don't have
** the mgr window manager installed
*/
V_visual(d)
int d;
{
Z_fatal("visual mode is not available on this machine\n");
return(-d); /* boiler plate */
}
void
V_cleanup()
{
}
#endif

17
utils/Spiff/visual.h Normal file
View File

@ -0,0 +1,17 @@
/* 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 V_INCLUDED
extern void V_cleanup();
#define V_INCLUDED
#endif