Add the Spiff fp-aware diff utility from Bellcore
authorBrian Gaeke <gaeke@uiuc.edu>
Mon, 12 Apr 2004 22:53:24 +0000 (22:53 +0000)
committerBrian Gaeke <gaeke@uiuc.edu>
Mon, 12 Apr 2004 22:53:24 +0000 (22:53 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@12875 91177308-0d34-0410-b5e6-96231b3b80d8

38 files changed:
utils/Spiff/LICENSE.TXT [new file with mode: 0644]
utils/Spiff/Makefile [new file with mode: 0644]
utils/Spiff/README [new file with mode: 0644]
utils/Spiff/command.c [new file with mode: 0644]
utils/Spiff/command.h [new file with mode: 0644]
utils/Spiff/comment.c [new file with mode: 0644]
utils/Spiff/comment.h [new file with mode: 0644]
utils/Spiff/compare.c [new file with mode: 0644]
utils/Spiff/compare.h [new file with mode: 0644]
utils/Spiff/edit.h [new file with mode: 0644]
utils/Spiff/exact.c [new file with mode: 0644]
utils/Spiff/exact.h [new file with mode: 0644]
utils/Spiff/flagdefs.h [new file with mode: 0644]
utils/Spiff/float.c [new file with mode: 0644]
utils/Spiff/float.h [new file with mode: 0644]
utils/Spiff/floatrep.c [new file with mode: 0644]
utils/Spiff/floatrep.h [new file with mode: 0644]
utils/Spiff/line.c [new file with mode: 0644]
utils/Spiff/line.h [new file with mode: 0644]
utils/Spiff/miller.c [new file with mode: 0644]
utils/Spiff/miller.h [new file with mode: 0644]
utils/Spiff/misc.c [new file with mode: 0644]
utils/Spiff/misc.h [new file with mode: 0644]
utils/Spiff/output.c [new file with mode: 0644]
utils/Spiff/output.h [new file with mode: 0644]
utils/Spiff/paper.ms [new file with mode: 0644]
utils/Spiff/parse.c [new file with mode: 0644]
utils/Spiff/parse.h [new file with mode: 0644]
utils/Spiff/spiff.1 [new file with mode: 0644]
utils/Spiff/spiff.c [new file with mode: 0644]
utils/Spiff/strings.c [new file with mode: 0644]
utils/Spiff/strings.h [new file with mode: 0644]
utils/Spiff/token.c [new file with mode: 0644]
utils/Spiff/token.h [new file with mode: 0644]
utils/Spiff/tol.c [new file with mode: 0644]
utils/Spiff/tol.h [new file with mode: 0644]
utils/Spiff/visual.c [new file with mode: 0644]
utils/Spiff/visual.h [new file with mode: 0644]

diff --git a/utils/Spiff/LICENSE.TXT b/utils/Spiff/LICENSE.TXT
new file mode 100644 (file)
index 0000000..8684d40
--- /dev/null
@@ -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 (file)
index 0000000..c0fd230
--- /dev/null
@@ -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 (file)
index 0000000..e559b50
--- /dev/null
@@ -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 (file)
index 0000000..6c9da47
--- /dev/null
@@ -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 (file)
index 0000000..2fa0723
--- /dev/null
@@ -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 (file)
index 0000000..f9a3827
--- /dev/null
@@ -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 (file)
index 0000000..01d8423
--- /dev/null
@@ -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 (file)
index 0000000..90af24b
--- /dev/null
@@ -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 (file)
index 0000000..b729979
--- /dev/null
@@ -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 (file)
index 0000000..5f28fcc
--- /dev/null
@@ -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 (file)
index 0000000..3bcce5c
--- /dev/null
@@ -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 (file)
index 0000000..a613ce8
--- /dev/null
@@ -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 (file)
index 0000000..ca1b5d0
--- /dev/null
@@ -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 (file)
index 0000000..f6f9e87
--- /dev/null
@@ -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 (file)
index 0000000..9a96255
--- /dev/null
@@ -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 (file)
index 0000000..4847f47
--- /dev/null
@@ -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 (file)
index 0000000..fa1f411
--- /dev/null
@@ -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 (file)
index 0000000..947f1fa
--- /dev/null
@@ -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 (file)
index 0000000..fdc05fe
--- /dev/null
@@ -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 (file)
index 0000000..63c7660
--- /dev/null
@@ -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 (file)
index 0000000..02abd17
--- /dev/null
@@ -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 (file)
index 0000000..3bdf164
--- /dev/null
@@ -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 (file)
index 0000000..c4cf543
--- /dev/null
@@ -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 (file)
index 0000000..3db9044
--- /dev/null
@@ -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 (file)
index 0000000..1bc6eb8
--- /dev/null
@@ -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 (file)
index 0000000..7c6ec29
--- /dev/null
@@ -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 (file)
index 0000000..55722b7
--- /dev/null
@@ -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 (file)
index 0000000..954833b
--- /dev/null
@@ -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 (file)
index 0000000..161b07f
--- /dev/null
@@ -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 (file)
index 0000000..eb0c755
--- /dev/null
@@ -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 (file)
index 0000000..0774563
--- /dev/null
@@ -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 (file)
index 0000000..b265680
--- /dev/null
@@ -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 (file)
index 0000000..fa64c07
--- /dev/null
@@ -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 (file)
index 0000000..f186186
--- /dev/null
@@ -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 (file)
index 0000000..6812355
--- /dev/null
@@ -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 (file)
index 0000000..87c2911
--- /dev/null
@@ -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 (file)
index 0000000..93fb38e
--- /dev/null
@@ -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 (file)
index 0000000..dfa69ae
--- /dev/null
@@ -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