aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrian Gaeke <gaeke@uiuc.edu>2004-04-12 22:53:24 +0000
committerBrian Gaeke <gaeke@uiuc.edu>2004-04-12 22:53:24 +0000
commit0df5f9fb7ebd0d4e8e068d642587e9d036f00885 (patch)
tree1262636c286a51243dec57747d034cd2d05ec989
parent5f0b51547ca71f496208ac23b92481132efa0def (diff)
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
-rw-r--r--utils/Spiff/LICENSE.TXT18
-rw-r--r--utils/Spiff/Makefile6
-rw-r--r--utils/Spiff/README264
-rw-r--r--utils/Spiff/command.c193
-rw-r--r--utils/Spiff/command.h18
-rw-r--r--utils/Spiff/comment.c307
-rw-r--r--utils/Spiff/comment.h84
-rw-r--r--utils/Spiff/compare.c209
-rw-r--r--utils/Spiff/compare.h14
-rw-r--r--utils/Spiff/edit.h43
-rw-r--r--utils/Spiff/exact.c92
-rw-r--r--utils/Spiff/exact.h17
-rw-r--r--utils/Spiff/flagdefs.h29
-rw-r--r--utils/Spiff/float.c811
-rw-r--r--utils/Spiff/float.h35
-rw-r--r--utils/Spiff/floatrep.c32
-rw-r--r--utils/Spiff/floatrep.h66
-rw-r--r--utils/Spiff/line.c169
-rw-r--r--utils/Spiff/line.h113
-rw-r--r--utils/Spiff/miller.c127
-rw-r--r--utils/Spiff/miller.h17
-rw-r--r--utils/Spiff/misc.c119
-rw-r--r--utils/Spiff/misc.h49
-rw-r--r--utils/Spiff/output.c558
-rw-r--r--utils/Spiff/output.h17
-rw-r--r--utils/Spiff/paper.ms819
-rw-r--r--utils/Spiff/parse.c802
-rw-r--r--utils/Spiff/parse.h10
-rw-r--r--utils/Spiff/spiff.1454
-rw-r--r--utils/Spiff/spiff.c341
-rw-r--r--utils/Spiff/strings.c162
-rw-r--r--utils/Spiff/strings.h20
-rw-r--r--utils/Spiff/token.c37
-rw-r--r--utils/Spiff/token.h81
-rw-r--r--utils/Spiff/tol.c361
-rw-r--r--utils/Spiff/tol.h64
-rw-r--r--utils/Spiff/visual.c411
-rw-r--r--utils/Spiff/visual.h17
38 files changed, 6986 insertions, 0 deletions
diff --git a/utils/Spiff/LICENSE.TXT b/utils/Spiff/LICENSE.TXT
new file mode 100644
index 0000000000..8684d4047d
--- /dev/null
+++ b/utils/Spiff/LICENSE.TXT
@@ -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.
diff --git a/utils/Spiff/Makefile b/utils/Spiff/Makefile
new file mode 100644
index 0000000000..c0fd230156
--- /dev/null
+++ b/utils/Spiff/Makefile
@@ -0,0 +1,6 @@
+LEVEL = ../..
+TOOLNAME = spiff
+
+include $(LEVEL)/Makefile.common
+
+CPPFLAGS += -DATT -DNOCHATTER
diff --git a/utils/Spiff/README b/utils/Spiff/README
new file mode 100644
index 0000000000..e559b500f3
--- /dev/null
+++ b/utils/Spiff/README
@@ -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
diff --git a/utils/Spiff/command.c b/utils/Spiff/command.c
new file mode 100644
index 0000000000..6c9da47ac9
--- /dev/null
+++ b/utils/Spiff/command.c
@@ -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);
+}
+
diff --git a/utils/Spiff/command.h b/utils/Spiff/command.h
new file mode 100644
index 0000000000..2fa0723ef5
--- /dev/null
+++ b/utils/Spiff/command.h
@@ -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
diff --git a/utils/Spiff/comment.c b/utils/Spiff/comment.c
new file mode 100644
index 0000000000..f9a3827589
--- /dev/null
+++ b/utils/Spiff/comment.c
@@ -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);
+}
diff --git a/utils/Spiff/comment.h b/utils/Spiff/comment.h
new file mode 100644
index 0000000000..01d8423021
--- /dev/null
+++ b/utils/Spiff/comment.h
@@ -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
diff --git a/utils/Spiff/compare.c b/utils/Spiff/compare.c
new file mode 100644
index 0000000000..90af24b3d8
--- /dev/null
+++ b/utils/Spiff/compare.c
@@ -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);
+}
diff --git a/utils/Spiff/compare.h b/utils/Spiff/compare.h
new file mode 100644
index 0000000000..b72997945d
--- /dev/null
+++ b/utils/Spiff/compare.h
@@ -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
diff --git a/utils/Spiff/edit.h b/utils/Spiff/edit.h
new file mode 100644
index 0000000000..5f28fccaea
--- /dev/null
+++ b/utils/Spiff/edit.h
@@ -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
+
+
diff --git a/utils/Spiff/exact.c b/utils/Spiff/exact.c
new file mode 100644
index 0000000000..3bcce5c694
--- /dev/null
+++ b/utils/Spiff/exact.c
@@ -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);
+}
diff --git a/utils/Spiff/exact.h b/utils/Spiff/exact.h
new file mode 100644
index 0000000000..a613ce81ca
--- /dev/null
+++ b/utils/Spiff/exact.h
@@ -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
diff --git a/utils/Spiff/flagdefs.h b/utils/Spiff/flagdefs.h
new file mode 100644
index 0000000000..ca1b5d06bf
--- /dev/null
+++ b/utils/Spiff/flagdefs.h
@@ -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
diff --git a/utils/Spiff/float.c b/utils/Spiff/float.c
new file mode 100644
index 0000000000..f6f9e87a0f
--- /dev/null
+++ b/utils/Spiff/float.c
@@ -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) ? '+': '-'));
+
+}
+*/
diff --git a/utils/Spiff/float.h b/utils/Spiff/float.h
new file mode 100644
index 0000000000..9a96255122
--- /dev/null
+++ b/utils/Spiff/float.h
@@ -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
diff --git a/utils/Spiff/floatrep.c b/utils/Spiff/floatrep.c
new file mode 100644
index 0000000000..4847f47723
--- /dev/null
+++ b/utils/Spiff/floatrep.c
@@ -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);
+}
+
diff --git a/utils/Spiff/floatrep.h b/utils/Spiff/floatrep.h
new file mode 100644
index 0000000000..fa1f411639
--- /dev/null
+++ b/utils/Spiff/floatrep.h
@@ -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
diff --git a/utils/Spiff/line.c b/utils/Spiff/line.c
new file mode 100644
index 0000000000..947f1faa48
--- /dev/null
+++ b/utils/Spiff/line.c
@@ -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);
+}
+
diff --git a/utils/Spiff/line.h b/utils/Spiff/line.h
new file mode 100644
index 0000000000..fdc05fe622
--- /dev/null
+++ b/utils/Spiff/line.h
@@ -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
diff --git a/utils/Spiff/miller.c b/utils/Spiff/miller.c
new file mode 100644
index 0000000000..63c7660a7a
--- /dev/null
+++ b/utils/Spiff/miller.c
@@ -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);
+}
diff --git a/utils/Spiff/miller.h b/utils/Spiff/miller.h
new file mode 100644
index 0000000000..02abd171d3
--- /dev/null
+++ b/utils/Spiff/miller.h
@@ -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
diff --git a/utils/Spiff/misc.c b/utils/Spiff/misc.c
new file mode 100644
index 0000000000..3bdf164f27
--- /dev/null
+++ b/utils/Spiff/misc.c
@@ -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);
+}
diff --git a/utils/Spiff/misc.h b/utils/Spiff/misc.h
new file mode 100644
index 0000000000..c4cf543bb9
--- /dev/null
+++ b/utils/Spiff/misc.h
@@ -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
diff --git a/utils/Spiff/output.c b/utils/Spiff/output.c
new file mode 100644
index 0000000000..3db9044a05
--- /dev/null
+++ b/utils/Spiff/output.c
@@ -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;
+}
diff --git a/utils/Spiff/output.h b/utils/Spiff/output.h
new file mode 100644
index 0000000000..1bc6eb8dc1
--- /dev/null
+++ b/utils/Spiff/output.h
@@ -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
diff --git a/utils/Spiff/paper.ms b/utils/Spiff/paper.ms
new file mode 100644
index 0000000000..7c6ec2943e
--- /dev/null
+++ b/utils/Spiff/paper.ms
@@ -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
diff --git a/utils/Spiff/parse.c b/utils/Spiff/parse.c
new file mode 100644
index 0000000000..55722b701d
--- /dev/null
+++ b/utils/Spiff/parse.c
@@ -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;
+}
diff --git a/utils/Spiff/parse.h b/utils/Spiff/parse.h
new file mode 100644
index 0000000000..954833bd5e
--- /dev/null
+++ b/utils/Spiff/parse.h
@@ -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();
diff --git a/utils/Spiff/spiff.1 b/utils/Spiff/spiff.1
new file mode 100644
index 0000000000..161b07ffa6
--- /dev/null
+++ b/utils/Spiff/spiff.1
@@ -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).
diff --git a/utils/Spiff/spiff.c b/utils/Spiff/spiff.c
new file mode 100644
index 0000000000..eb0c755887
--- /dev/null
+++ b/utils/Spiff/spiff.c
@@ -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];
+}
diff --git a/utils/Spiff/strings.c b/utils/Spiff/strings.c
new file mode 100644
index 0000000000..07745635c6
--- /dev/null
+++ b/utils/Spiff/strings.c
@@ -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);
+}
diff --git a/utils/Spiff/strings.h b/utils/Spiff/strings.h
new file mode 100644
index 0000000000..b265680047
--- /dev/null
+++ b/utils/Spiff/strings.h
@@ -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
diff --git a/utils/Spiff/token.c b/utils/Spiff/token.c
new file mode 100644
index 0000000000..fa64c07418
--- /dev/null
+++ b/utils/Spiff/token.c
@@ -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;
+ }
+}
diff --git a/utils/Spiff/token.h b/utils/Spiff/token.h
new file mode 100644
index 0000000000..f1861868bd
--- /dev/null
+++ b/utils/Spiff/token.h
@@ -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
diff --git a/utils/Spiff/tol.c b/utils/Spiff/tol.c
new file mode 100644
index 0000000000..6812355cc4
--- /dev/null
+++ b/utils/Spiff/tol.c
@@ -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);
+ }
+ }
+}
diff --git a/utils/Spiff/tol.h b/utils/Spiff/tol.h
new file mode 100644
index 0000000000..87c291133a
--- /dev/null
+++ b/utils/Spiff/tol.h
@@ -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
diff --git a/utils/Spiff/visual.c b/utils/Spiff/visual.c
new file mode 100644
index 0000000000..93fb38e4e0
--- /dev/null
+++ b/utils/Spiff/visual.c
@@ -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
diff --git a/utils/Spiff/visual.h b/utils/Spiff/visual.h
new file mode 100644
index 0000000000..dfa69aeadc
--- /dev/null
+++ b/utils/Spiff/visual.h
@@ -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