mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2025-04-12 23:37:33 +00:00
Add the Spiff fp-aware diff utility from Bellcore
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@12875 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
5f0b51547c
commit
0df5f9fb7e
utils/Spiff
18
utils/Spiff/LICENSE.TXT
Normal file
18
utils/Spiff/LICENSE.TXT
Normal file
@ -0,0 +1,18 @@
|
||||
The copyright below applies to Spiff 1.0 as redistributed as part of LLVM
|
||||
and/or its test suite.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
COPYRIGHT
|
||||
|
||||
Our lawyers advise the following:
|
||||
|
||||
Copyright (c) 1988 Bellcore
|
||||
All Rights Reserved
|
||||
Permission is granted to copy or use this program, EXCEPT that it
|
||||
may not be sold for profit, the copyright notice must be reproduced
|
||||
on copies, and credit should be given to Bellcore where it is due.
|
||||
BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
|
||||
|
||||
Given that all of the above seems to be very reasonable, there should be no
|
||||
reason for anyone to not play by the rules.
|
6
utils/Spiff/Makefile
Normal file
6
utils/Spiff/Makefile
Normal file
@ -0,0 +1,6 @@
|
||||
LEVEL = ../..
|
||||
TOOLNAME = spiff
|
||||
|
||||
include $(LEVEL)/Makefile.common
|
||||
|
||||
CPPFLAGS += -DATT -DNOCHATTER
|
264
utils/Spiff/README
Normal file
264
utils/Spiff/README
Normal file
@ -0,0 +1,264 @@
|
||||
INSTALLATION
|
||||
1) Change makefile settings to reflect
|
||||
ATT vs. BSD software
|
||||
termio vs. termcap
|
||||
MGR vs. no MGR (MGR is a BELLCORE produced
|
||||
window manager that is also available
|
||||
free to the public.)
|
||||
2) Then, just say "make".
|
||||
If you want to "make install", you should first
|
||||
change definition of INSDIR in the makefile
|
||||
|
||||
3) to test the software say
|
||||
|
||||
spiff Sample.1 Sample.2
|
||||
|
||||
spiff should find 4 differences and
|
||||
you should see the words "added", "deleted", "changed",
|
||||
and "altered" as well as four number in stand-out mode.
|
||||
|
||||
spiff Sample.1 Sample.2 | cat
|
||||
|
||||
should produce the same output, only the differences
|
||||
should be underlined However, on many terminals the underlining
|
||||
does not appear. So try the command
|
||||
|
||||
spiff Sample.1 Sample.2 | cat -v
|
||||
|
||||
or whatever the equivalent to cat -v is on your system.
|
||||
|
||||
A more complicated test set is found in Sample.3 and Sample.4
|
||||
These files show how to use embedded commands to do things
|
||||
like change the commenting convention and tolerances on the
|
||||
fly. Be sure to run the command with the -s option to spiff:
|
||||
|
||||
spiff -s 'command spiffword' Sample.3 Sample.4
|
||||
|
||||
These files by no means provide an exhaustive test of
|
||||
spiff's features. But they should give you some idea if things
|
||||
are working right.
|
||||
|
||||
This code (or it's closely related cousins) has been run on
|
||||
Vaxen running 4.3BSD, a CCI Power 6, some XENIX machines, and some
|
||||
other machines running System V derivatives as well as
|
||||
(thanks to eugene@ames.arpa) Cray, Amdahl and Convex machines.
|
||||
|
||||
4) Share and enjoy.
|
||||
|
||||
AUTHOR'S ADDRESS
|
||||
Please send complaints, comments, praise, bug reports, etc to
|
||||
Dan Nachbar
|
||||
Bell Communications Research (also known as BELLCORE)
|
||||
445 South St. Room 2B-389
|
||||
Morristown, NJ 07960
|
||||
|
||||
nachbar@bellcore.com
|
||||
or
|
||||
bellcore!nachbar
|
||||
or
|
||||
(201) 829-4392 (praise only, please)
|
||||
|
||||
OVERVIEW OF OPERATION
|
||||
|
||||
Each of two input files is read and stored in core.
|
||||
Then it is parsed into a series of tokens (literal strings and
|
||||
floating point numbers, white space is ignored).
|
||||
The token sequences are stored in core as well.
|
||||
After both files have been parsed, a differencing algorithm is applied to
|
||||
the token sequences. The differencing algorithm
|
||||
produces an edit script, which is then passed to an output routine.
|
||||
|
||||
SIZE LIMITS AND OTHER DEFAULTS
|
||||
file implementing limit name default value
|
||||
maximum number of lines lines.h _L_MAXLINES 10000
|
||||
per file
|
||||
maximum number of tokens token.h K_MAXTOKENS 50000
|
||||
per file
|
||||
maximum line length misc.h Z_LINELEN 1024
|
||||
maximum word length misc.h Z_WORDLEN 20
|
||||
(length of misc buffers for
|
||||
things like literal
|
||||
delimiters.
|
||||
NOT length of tokens which
|
||||
can be virtually any length)
|
||||
default absolute tolerance tol.h _T_ADEF "1e-10"
|
||||
default relative tolerance tol.h _T_RDEF "1e-10"
|
||||
maximum number of commands command.h _C_CMDMAX 100
|
||||
in effect at one time
|
||||
maximum number of commenting comment.h W_COMMAX 20
|
||||
conventions that can be
|
||||
in effect at one time
|
||||
(not including commenting
|
||||
conventions that are
|
||||
restricted to beginning
|
||||
of line)
|
||||
maximum number of commenting comment.h W_BOLMAX 20
|
||||
conventions that are
|
||||
restricted to beginning of
|
||||
line that are in effect at
|
||||
one time
|
||||
maximum number of literal comment.h W_LITMAX 20
|
||||
string conventions that
|
||||
can be in effect at one time
|
||||
maximum number of tolerances tol.h _T_TOLMAX 10
|
||||
that can be in effect at one
|
||||
time
|
||||
|
||||
|
||||
DIFFERENCES BETWEEN THE CURRENT VERSION AND THE ENCLOSED PAPER
|
||||
|
||||
The files paper.ms and paper.out contain the nroff -ms input and
|
||||
output respectively of a paper on spiff that was given the Summer '88
|
||||
USENIX conference in San Francisco. Since that time many changes
|
||||
have been made to the code. Many flags have changed and some have
|
||||
had their meanings reversed, see the enclosed man page for the current
|
||||
usage. Also, there is no longer control over the
|
||||
granularity of object used when applying the differencing algorithm.
|
||||
The current version of spiff always applies the differencing
|
||||
in terms of individual tokens. The -t flag controls how the edit script
|
||||
is printed. This arrangement more closely reflects the original intent
|
||||
of having multiple differencing granularities.
|
||||
|
||||
PERFORMANCE
|
||||
|
||||
Spiff is big and slow. It is big because all the storage is
|
||||
in core. It is a straightforward but boring task to move the temporary
|
||||
storage into a file. Someone who cares is invited to take on the job.
|
||||
Spiff is slow because whenever a choice had to be made between
|
||||
speed of operation and ease of coding, speed of operation almost always lost.
|
||||
As the program matures it will almost certainly get smaller and faster.
|
||||
Obvious performance enhancements have been avoided in order to make the
|
||||
program available as soon as possible.
|
||||
|
||||
COPYRIGHT
|
||||
|
||||
Our lawyers advise the following:
|
||||
|
||||
Copyright (c) 1988 Bellcore
|
||||
All Rights Reserved
|
||||
Permission is granted to copy or use this program, EXCEPT that it
|
||||
may not be sold for profit, the copyright notice must be reproduced
|
||||
on copies, and credit should be given to Bellcore where it is due.
|
||||
BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
|
||||
|
||||
Given that all of the above seems to be very reasonable, there should be no
|
||||
reason for anyone to not play by the rules.
|
||||
|
||||
|
||||
NAMING CONVENTIONS USED IN THE CODE
|
||||
|
||||
All symbols (functions, data declarations, macros) are named as follows:
|
||||
|
||||
L_foo -- for names exported to other modules
|
||||
and possibly used inside the module as well.
|
||||
_L_foo -- for names used by more than one routine
|
||||
within a module
|
||||
foo -- for names used inside a single routine.
|
||||
|
||||
Each module uses a different value for "L" --
|
||||
module files letter used implements
|
||||
spiff.c Y top level routines
|
||||
misc.[ch] Z various routines used throughout
|
||||
strings.[ch] S routines for handling strings
|
||||
edit.h E list of changes found and printed
|
||||
tol.[ch] T tolerances for real numbers
|
||||
token.[ch] K storage for objects
|
||||
float.[ch] F manipulation of floats
|
||||
floatrep.[ch] R representation of floats
|
||||
line.[ch] L storage for input lines
|
||||
parse.[ch] P parse for input files
|
||||
command.[ch] C storage and recognition of commands
|
||||
comment.[ch] W comment list maintenance
|
||||
compare.[ch] X comparisons of a single token
|
||||
exact.[ch] Q exact match differencing algorithm
|
||||
miller.[ch] G miller/myers differencing algorithm
|
||||
output.[ch] O print listing of differences
|
||||
flagdefs.h U define flag bits that are used in
|
||||
several of the other modules.
|
||||
These #defines could have been
|
||||
included in misc.c, but were separated
|
||||
out because of their explicit
|
||||
communication function.
|
||||
visual.[ch] V screen oriented display for MGR
|
||||
window manager, also contains
|
||||
dummy routines for people who don't
|
||||
have MGR
|
||||
|
||||
I haven't cleaned up visual.c yet. It probably doesn't even compile
|
||||
in this version anyway. But since most people don't have mgr, this
|
||||
isn't urgent.
|
||||
|
||||
NON-OBVIOUS DATA STRUCTURES
|
||||
|
||||
The Floating Point Representation
|
||||
|
||||
Floating point numbers are stored in a struct R_flstr
|
||||
The fractional part is often called the mantissa.
|
||||
|
||||
The structure consists of
|
||||
a flag for the sign of the factional part
|
||||
the exponent in binary
|
||||
a character string containing the fractional part
|
||||
|
||||
The structure could be converted to a float via
|
||||
atof(strcat(".",mantissa)) * (10^exponent)
|
||||
|
||||
To be properly formed, the mantissa string must:
|
||||
start with a digit between 1 and 9 (i.e. no leading zeros)
|
||||
except for the zero, in which case the mantissa is exactly "0"
|
||||
for the special case of zero, the exponent is always 0, and the
|
||||
sign is always positive. (i.e. no negative 0)
|
||||
|
||||
In other words, (except for the value 0)
|
||||
the mantissa is a fractional number ranging
|
||||
between 0.1 (inclusive) and 1.0 (exclusive).
|
||||
The exponent is interpreted as a power of 10.
|
||||
|
||||
Lines
|
||||
there are three sets of lines:
|
||||
implemented in line.c and line.h
|
||||
real_lines --
|
||||
the lines as they come from the file
|
||||
content_lines --
|
||||
a subset of reallines that excluding embedded commands
|
||||
implemented in token.c and token.h
|
||||
token_lines --
|
||||
a subset of content_lines consisting of those lines that
|
||||
have tokens that begin on them (literals can go on for
|
||||
more than one line)
|
||||
i.e. content_lines excluding comments and blank lines.
|
||||
|
||||
|
||||
THE STATE OF THE CODE
|
||||
Things that should be added
|
||||
visual mode should handle tabs and wrapped lines
|
||||
handling huge files in chunks when in using the ordinal match
|
||||
algorithm. right now you have to parse and then diff the
|
||||
whole thing before you get any output. often, you run out of memory.
|
||||
|
||||
Things that would be nice to add
|
||||
output should optionally be expressed in real line numbers
|
||||
(i.e. including command lines)
|
||||
at present, all storage is in core. there should
|
||||
be a compile time decision to allow temporary storage
|
||||
in files rather than core.
|
||||
that way the user could decide how to handle the
|
||||
speed/space tradeoff
|
||||
a front end that looked like diff should be added so that
|
||||
one could drop spiff into existing shell scripts
|
||||
the parser converts floats into their internal form even when
|
||||
it isn't necessary.
|
||||
in the miller/myer code, the code should check for matching
|
||||
end sequences. it currently looks matching beginning
|
||||
sequences.
|
||||
|
||||
Minor programming improvements (programming botches)
|
||||
some of the #defines should really be enumerated types
|
||||
all the routines in strings.c that alter the data at the end of
|
||||
a pointer but return void should just return the correct
|
||||
data. the current arrangement is a historical artifact
|
||||
of the days when these routines returned a status code.
|
||||
but then the code was never examined,
|
||||
so i made them void . . .
|
||||
comments should be added to the miller/myer code
|
||||
in visual mode, ask for font by name rather than number
|
193
utils/Spiff/command.c
Normal file
193
utils/Spiff/command.c
Normal file
@ -0,0 +1,193 @@
|
||||
/* Copyright (c) 1988 Bellcore
|
||||
** All Rights Reserved
|
||||
** Permission is granted to copy or use this program, EXCEPT that it
|
||||
** may not be sold for profit, the copyright notice must be reproduced
|
||||
** on copies, and credit should be given to Bellcore where it is due.
|
||||
** BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef lint
|
||||
static char rcsid[]= "$Header$";
|
||||
#endif
|
||||
|
||||
|
||||
#include "misc.h"
|
||||
#include "tol.h"
|
||||
#include "comment.h"
|
||||
#include "command.h"
|
||||
#include "strings.h"
|
||||
#include "parse.h"
|
||||
|
||||
/*
|
||||
** storage for the string that signals an embedded command
|
||||
*/
|
||||
static char _C_cmdword[Z_WORDLEN];
|
||||
|
||||
/*
|
||||
** storage for the command script
|
||||
*/
|
||||
static int _C_nextcmd = 0;
|
||||
static char *_C_cmds[_C_CMDMAX];
|
||||
|
||||
|
||||
/*
|
||||
** add a string to the command buffer
|
||||
*/
|
||||
void
|
||||
C_addcmd(str)
|
||||
char *str;
|
||||
{
|
||||
S_savestr(&_C_cmds[_C_nextcmd++],str);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
** execute a single command
|
||||
*/
|
||||
static void
|
||||
_C_do_a_cmd(str)
|
||||
char *str;
|
||||
{
|
||||
/*
|
||||
** place holder for the beginning of the string
|
||||
*/
|
||||
char *beginning = str;
|
||||
|
||||
S_skipspace(&str);
|
||||
|
||||
/*
|
||||
** set the command string to allow embedded commands
|
||||
*/
|
||||
if (!S_wordcmp(str,"command"))
|
||||
{
|
||||
S_nextword(&str);
|
||||
if (strlen(str) >= Z_WORDLEN)
|
||||
{
|
||||
Z_fatal("command word is too long");
|
||||
}
|
||||
S_wordcpy(_C_cmdword,str);
|
||||
}
|
||||
/*
|
||||
** set the tolerances
|
||||
*/
|
||||
else if (!S_wordcmp(str,"tol"))
|
||||
{
|
||||
S_nextword(&str);
|
||||
T_tolline(str);
|
||||
}
|
||||
/*
|
||||
** add a comment specification
|
||||
*/
|
||||
else if (!S_wordcmp(str,"comment"))
|
||||
{
|
||||
S_nextword(&str);
|
||||
if (strlen(str) >= Z_WORDLEN)
|
||||
{
|
||||
Z_fatal("command word is too long");
|
||||
}
|
||||
W_addcom(str,0);
|
||||
}
|
||||
else if (!S_wordcmp(str,"nestcom"))
|
||||
{
|
||||
S_nextword(&str);
|
||||
if (strlen(str) >= Z_WORDLEN)
|
||||
{
|
||||
Z_fatal("command word is too long");
|
||||
}
|
||||
W_addcom(str,1);
|
||||
}
|
||||
/*
|
||||
** add a literal string specification
|
||||
*/
|
||||
else if (!S_wordcmp(str,"literal"))
|
||||
{
|
||||
S_nextword(&str);
|
||||
if (strlen(str) >= Z_WORDLEN)
|
||||
{
|
||||
Z_fatal("command word is too long");
|
||||
}
|
||||
W_addlit(str);
|
||||
}
|
||||
else if (!S_wordcmp(str,"resetcomments"))
|
||||
{
|
||||
W_clearcoms();
|
||||
}
|
||||
else if (!S_wordcmp(str,"resetliterals"))
|
||||
{
|
||||
W_clearlits();
|
||||
}
|
||||
else if (!S_wordcmp(str,"beginchar"))
|
||||
{
|
||||
S_nextword(&str);
|
||||
W_setbolchar(*str);
|
||||
}
|
||||
else if (!S_wordcmp(str,"endchar"))
|
||||
{
|
||||
S_nextword(&str);
|
||||
W_seteolchar(*str);
|
||||
}
|
||||
else if (!S_wordcmp(str,"addalpha"))
|
||||
{
|
||||
S_nextword(&str);
|
||||
P_addalpha(str);
|
||||
}
|
||||
else if ((0 == strlen(str)) || !S_wordcmp(str,"rem")
|
||||
|| ('#' == *str))
|
||||
{
|
||||
/* do nothing */
|
||||
}
|
||||
else
|
||||
{
|
||||
(void) sprintf(Z_err_buf,
|
||||
"don't understand command %s\n",
|
||||
beginning);
|
||||
Z_fatal(Z_err_buf);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
** execute the commands in the command buffer
|
||||
*/
|
||||
void
|
||||
C_docmds()
|
||||
{
|
||||
int i;
|
||||
for (i=0;i<_C_nextcmd;i++)
|
||||
{
|
||||
_C_do_a_cmd(_C_cmds[i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
** disable embedded command key word recognition
|
||||
*/
|
||||
void
|
||||
C_clear_cmd()
|
||||
{
|
||||
_C_cmdword[0] = '\0';
|
||||
return;
|
||||
}
|
||||
|
||||
#define inline spiff_inline
|
||||
int
|
||||
C_is_cmd(inline)
|
||||
char *inline;
|
||||
{
|
||||
char *ptr;
|
||||
/*
|
||||
** see if this is a command line
|
||||
** and if so, do the command right away
|
||||
*/
|
||||
if (('\0' != _C_cmdword[0]) && (!S_wordcmp(inline,_C_cmdword)))
|
||||
{
|
||||
ptr = inline;
|
||||
S_nextword(&ptr);
|
||||
_C_do_a_cmd(ptr);
|
||||
return(1);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
18
utils/Spiff/command.h
Normal file
18
utils/Spiff/command.h
Normal file
@ -0,0 +1,18 @@
|
||||
/* Copyright (c) 1988 Bellcore
|
||||
** All Rights Reserved
|
||||
** Permission is granted to copy or use this program, EXCEPT that it
|
||||
** may not be sold for profit, the copyright notice must be reproduced
|
||||
** on copies, and credit should be given to Bellcore where it is due.
|
||||
** BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
|
||||
*/
|
||||
|
||||
#ifndef C_INCLUDED
|
||||
extern int C_is_cmd();
|
||||
extern void C_clear_cmd();
|
||||
extern void C_addcmd();
|
||||
extern void C_docmds();
|
||||
|
||||
#define _C_CMDMAX 100
|
||||
|
||||
#define C_INCLUDED
|
||||
#endif
|
307
utils/Spiff/comment.c
Normal file
307
utils/Spiff/comment.c
Normal file
@ -0,0 +1,307 @@
|
||||
/* Copyright (c) 1988 Bellcore
|
||||
** All Rights Reserved
|
||||
** Permission is granted to copy or use this program, EXCEPT that it
|
||||
** may not be sold for profit, the copyright notice must be reproduced
|
||||
** on copies, and credit should be given to Bellcore where it is due.
|
||||
** BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef lint
|
||||
static char rcsid[]= "$Header$";
|
||||
#endif
|
||||
|
||||
|
||||
#include "misc.h"
|
||||
#include "comment.h"
|
||||
#include "strings.h"
|
||||
|
||||
/*
|
||||
** storage for the comment specifiers that can appear
|
||||
** anywhere on a line
|
||||
*/
|
||||
static int _W_nextcom = 0;
|
||||
_W_comstruct _W_coms[_W_COMMAX];
|
||||
|
||||
/*
|
||||
** storage for comment specifiers that are examined only at the
|
||||
** beginning of each line
|
||||
*/
|
||||
static int _W_nextbol = 0;
|
||||
_W_bolstruct _W_bols[_W_BOLMAX];
|
||||
|
||||
/*
|
||||
** storage for delimiters of literal strings
|
||||
*/
|
||||
static int _W_nextlit = 0;
|
||||
_W_litstruct _W_lits[_W_LITMAX];
|
||||
|
||||
/*
|
||||
** storage for characters to specify beginning and end of line
|
||||
** in the comment and literal commands
|
||||
*/
|
||||
char _W_bolchar = '^';
|
||||
char _W_eolchar = '$';
|
||||
|
||||
|
||||
/*
|
||||
** build up a list of comment delimiters
|
||||
*/
|
||||
void
|
||||
W_addcom(str,nestflag)
|
||||
char *str;
|
||||
int nestflag;
|
||||
{
|
||||
/*
|
||||
** check for comments that begin at the beginning of line
|
||||
*/
|
||||
if (*str == _W_bolchar)
|
||||
{
|
||||
if (_W_nextbol >= _W_BOLMAX)
|
||||
Z_fatal("too many beginning of line comment delimiter sets");
|
||||
|
||||
str++; /*skip the bol char */
|
||||
S_wordcpy(_W_bols[_W_nextbol].begin,str);
|
||||
|
||||
S_nextword(&str);
|
||||
|
||||
if (*str == _W_eolchar)
|
||||
{
|
||||
(void) strcpy(_W_bols[_W_nextbol].end,"\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
S_wordcpy(_W_bols[_W_nextbol].end,str);
|
||||
}
|
||||
|
||||
S_nextword(&str);
|
||||
S_wordcpy(_W_bols[_W_nextbol].escape,str);
|
||||
|
||||
/*
|
||||
**
|
||||
*/
|
||||
if (nestflag)
|
||||
Z_complain("begining of line comment won't nest");
|
||||
|
||||
_W_nextbol++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_W_nextcom >= _W_COMMAX)
|
||||
Z_fatal("too many comment delimiter sets");
|
||||
|
||||
S_wordcpy(_W_coms[_W_nextcom].begin,str);
|
||||
|
||||
S_nextword(&str);
|
||||
|
||||
if (*str == _W_eolchar)
|
||||
{
|
||||
(void) strcpy(_W_coms[_W_nextbol].end,"\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
S_wordcpy(_W_coms[_W_nextbol].end,str);
|
||||
}
|
||||
|
||||
S_nextword(&str);
|
||||
S_wordcpy(_W_coms[_W_nextcom].escape,str);
|
||||
|
||||
_W_coms[_W_nextcom].nestbit = nestflag;
|
||||
|
||||
_W_nextcom++;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** clear the comment delimiter storage
|
||||
*/
|
||||
void
|
||||
W_clearcoms()
|
||||
{
|
||||
_W_nextcom = 0;
|
||||
_W_nextbol = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
** build up the list of literal delimiters
|
||||
*/
|
||||
void
|
||||
W_addlit(str)
|
||||
char *str;
|
||||
{
|
||||
if (_W_nextlit >= _W_LITMAX)
|
||||
Z_fatal("too many literal delimiter sets");
|
||||
|
||||
S_wordcpy(_W_lits[_W_nextlit].begin,str);
|
||||
|
||||
S_nextword(&str);
|
||||
S_wordcpy(_W_lits[_W_nextlit].end,str);
|
||||
|
||||
S_nextword(&str);
|
||||
S_wordcpy(_W_lits[_W_nextlit].escape,str);
|
||||
|
||||
_W_nextlit++;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
** clear the literal delimiter storage
|
||||
*/
|
||||
void
|
||||
W_clearlits()
|
||||
{
|
||||
_W_nextlit = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static _W_bolstruct bol_scratch;
|
||||
|
||||
static void
|
||||
_W_copybol(to,from)
|
||||
W_bol to,from;
|
||||
{
|
||||
(void) strcpy(to->begin,from->begin);
|
||||
(void) strcpy(to->end,from->end);
|
||||
(void) strcpy(to->escape,from->escape);
|
||||
}
|
||||
|
||||
W_bol
|
||||
W_isbol(str)
|
||||
char *str;
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0;i<_W_nextbol;i++)
|
||||
{
|
||||
if(!S_wordcmp(str,_W_bols[i].begin))
|
||||
{
|
||||
_W_copybol(&bol_scratch,&_W_bols[i]);
|
||||
return(&bol_scratch);
|
||||
}
|
||||
}
|
||||
return(W_BOLNULL);
|
||||
}
|
||||
|
||||
W_is_bol(ptr)
|
||||
W_bol ptr;
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0;i<_W_nextbol;i++)
|
||||
{
|
||||
if(!S_wordcmp(ptr->begin,_W_bols[i].begin) &&
|
||||
!S_wordcmp(ptr->end,_W_bols[i].end) &&
|
||||
!S_wordcmp(ptr->escape,_W_bols[i].escape))
|
||||
{
|
||||
return(1);
|
||||
}
|
||||
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
static _W_litstruct lit_scratch;
|
||||
|
||||
static void
|
||||
_W_copylit(to,from)
|
||||
W_lit to,from;
|
||||
{
|
||||
(void) strcpy(to->begin,from->begin);
|
||||
(void) strcpy(to->end,from->end);
|
||||
(void) strcpy(to->escape,from->escape);
|
||||
}
|
||||
|
||||
W_lit
|
||||
W_islit(str)
|
||||
char *str;
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0;i<_W_nextlit;i++)
|
||||
{
|
||||
if(!S_wordcmp(str,_W_lits[i].begin))
|
||||
{
|
||||
_W_copylit(&lit_scratch,&_W_lits[i]);
|
||||
return(&lit_scratch);
|
||||
}
|
||||
}
|
||||
return(W_LITNULL);
|
||||
}
|
||||
|
||||
W_is_lit(ptr)
|
||||
W_lit ptr;
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0;i<_W_nextlit;i++)
|
||||
{
|
||||
if(!S_wordcmp(ptr->begin,_W_lits[i].begin) &&
|
||||
!S_wordcmp(ptr->end,_W_lits[i].end) &&
|
||||
!S_wordcmp(ptr->escape,_W_lits[i].escape))
|
||||
{
|
||||
return(1);
|
||||
}
|
||||
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
static _W_comstruct com_scratch;
|
||||
|
||||
static void
|
||||
_W_copycom(to,from)
|
||||
W_com to,from;
|
||||
{
|
||||
(void) strcpy(to->begin,from->begin);
|
||||
(void) strcpy(to->end,from->end);
|
||||
(void) strcpy(to->escape,from->escape);
|
||||
to->nestbit = from->nestbit;
|
||||
}
|
||||
|
||||
W_com
|
||||
W_iscom(str)
|
||||
char *str;
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0;i<_W_nextcom;i++)
|
||||
{
|
||||
if(!S_wordcmp(str,_W_coms[i].begin))
|
||||
{
|
||||
_W_copycom(&com_scratch,&_W_coms[i]);
|
||||
return(&com_scratch);
|
||||
}
|
||||
}
|
||||
return(W_COMNULL);
|
||||
}
|
||||
|
||||
W_is_com(ptr)
|
||||
W_com ptr;
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0;i<_W_nextcom;i++)
|
||||
{
|
||||
if(!S_wordcmp(ptr->begin,_W_coms[i].begin) &&
|
||||
!S_wordcmp(ptr->end,_W_coms[i].end) &&
|
||||
!S_wordcmp(ptr->escape,_W_coms[i].escape) &&
|
||||
ptr->nestbit == _W_coms[i].nestbit)
|
||||
{
|
||||
return(1);
|
||||
}
|
||||
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
W_is_nesting(ptr)
|
||||
W_com ptr;
|
||||
{
|
||||
return(ptr->nestbit);
|
||||
}
|
84
utils/Spiff/comment.h
Normal file
84
utils/Spiff/comment.h
Normal file
@ -0,0 +1,84 @@
|
||||
/* Copyright (c) 1988 Bellcore
|
||||
** All Rights Reserved
|
||||
** Permission is granted to copy or use this program, EXCEPT that it
|
||||
** may not be sold for profit, the copyright notice must be reproduced
|
||||
** on copies, and credit should be given to Bellcore where it is due.
|
||||
** BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
|
||||
*/
|
||||
|
||||
#ifndef W_INCLUDED
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define _W_COMWORD 16
|
||||
#define _W_COMMAX 20
|
||||
#define _W_BOLMAX 20
|
||||
#define _W_LITMAX 20
|
||||
|
||||
/*
|
||||
** these three data structures used to be much
|
||||
** different. eventually, the differences
|
||||
** have disappeared as the code has evolved.
|
||||
** obviously, they should now be collapsed.
|
||||
** someday . . .
|
||||
*/
|
||||
typedef struct {
|
||||
char begin[_W_COMWORD];
|
||||
char end[_W_COMWORD];
|
||||
char escape[_W_COMWORD];
|
||||
} _W_bolstruct, *W_bol;
|
||||
|
||||
typedef struct {
|
||||
char begin[_W_COMWORD];
|
||||
char end[_W_COMWORD];
|
||||
char escape[_W_COMWORD];
|
||||
int nestbit;
|
||||
} _W_comstruct, *W_com;
|
||||
|
||||
typedef struct {
|
||||
char begin[_W_COMWORD];
|
||||
char end[_W_COMWORD];
|
||||
char escape[_W_COMWORD];
|
||||
} _W_litstruct, *W_lit;
|
||||
|
||||
#define W_bolbegin(ptr) (ptr->begin)
|
||||
#define W_bolend(ptr) (ptr->end)
|
||||
#define W_bolescape(ptr) (ptr->escape)
|
||||
|
||||
#define W_litbegin(ptr) (ptr->begin)
|
||||
#define W_litend(ptr) (ptr->end)
|
||||
#define W_litescape(ptr) (ptr->escape)
|
||||
|
||||
#define W_combegin(ptr) (ptr->begin)
|
||||
#define W_comend(ptr) (ptr->end)
|
||||
#define W_comescape(ptr) (ptr->escape)
|
||||
|
||||
extern char _W_bolchar;
|
||||
extern char _W_eolchar;
|
||||
|
||||
#define W_setbolchar(x) (_W_bolchar = x)
|
||||
#define W_seteolchar(x) (_W_eolchar = x)
|
||||
|
||||
extern W_bol W_isbol();
|
||||
extern W_lit W_islit();
|
||||
extern W_com W_iscom();
|
||||
|
||||
extern int W_is_bol();
|
||||
extern int W_is_lit();
|
||||
extern int W_is_com();
|
||||
|
||||
extern _W_bolstruct _W_bols[];
|
||||
extern _W_litstruct _W_lits[];
|
||||
extern _W_comstruct _W_coms[];
|
||||
|
||||
extern void W_clearcoms();
|
||||
extern void W_clearlits();
|
||||
extern void W_addcom();
|
||||
extern void W_addlit();
|
||||
|
||||
#define W_BOLNULL ((W_bol)0)
|
||||
#define W_COMNULL ((W_com)0)
|
||||
#define W_LITNULL ((W_lit)0)
|
||||
|
||||
#define W_INCLUDED
|
||||
#endif
|
209
utils/Spiff/compare.c
Normal file
209
utils/Spiff/compare.c
Normal file
@ -0,0 +1,209 @@
|
||||
/* Copyright (c) 1988 Bellcore
|
||||
** All Rights Reserved
|
||||
** Permission is granted to copy or use this program, EXCEPT that it
|
||||
** may not be sold for profit, the copyright notice must be reproduced
|
||||
** on copies, and credit should be given to Bellcore where it is due.
|
||||
** BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef lint
|
||||
static char rcsid[]= "$Header$";
|
||||
#endif
|
||||
|
||||
#include "misc.h"
|
||||
#include "flagdefs.h"
|
||||
#include "tol.h"
|
||||
#include "token.h"
|
||||
#include "line.h"
|
||||
#include "float.h"
|
||||
#include "compare.h"
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
X_com(a,b,flags)
|
||||
int a,b,flags;
|
||||
{
|
||||
K_token atmp,btmp;
|
||||
|
||||
atmp = K_gettoken(0,a);
|
||||
btmp = K_gettoken(1,b);
|
||||
if(flags & U_BYTE_COMPARE)
|
||||
{
|
||||
return(_X_strcmp(K_gettext(atmp),K_gettext(btmp),flags));
|
||||
}
|
||||
else
|
||||
{
|
||||
return(_X_cmptokens(atmp,btmp,flags));
|
||||
}
|
||||
#ifndef lint
|
||||
Z_fatal("this line should never be reached in com");
|
||||
return(-1); /* Z_fatal never returns, but i need a this line
|
||||
here to stop lint from complaining */
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** same as strcmp() except that case can be optionally ignored
|
||||
*/
|
||||
static int
|
||||
_X_strcmp(s1,s2,flags)
|
||||
char *s1,*s2;
|
||||
int flags;
|
||||
{
|
||||
if (flags & U_NO_CASE)
|
||||
{
|
||||
|
||||
for (;('\0' != s1) && ('\0' != *s2);s1++,s2++)
|
||||
{
|
||||
if(isalpha(*s1) && isalpha(*s2))
|
||||
{
|
||||
if(tolower(*s1) != tolower(*s2))
|
||||
{
|
||||
return(1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(*s1!=*s2)
|
||||
{
|
||||
return(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return(*s1 != *s2);
|
||||
}
|
||||
else
|
||||
{
|
||||
return(strcmp(s1,s2));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** routine to compare two tokens
|
||||
*/
|
||||
static int
|
||||
_X_cmptokens(p1,p2,flags)
|
||||
K_token p1, p2;
|
||||
int flags;
|
||||
{
|
||||
if (K_gettype(p1) != K_gettype(p2))
|
||||
{
|
||||
return(1);
|
||||
}
|
||||
|
||||
switch (K_gettype(p1))
|
||||
{
|
||||
case K_LIT:
|
||||
return(_X_strcmp(K_gettext(p1),K_gettext(p2),flags));
|
||||
case K_FLO_NUM:
|
||||
return(_X_floatdiff(K_getfloat(p1),
|
||||
K_getfloat(p2),
|
||||
T_picktol(K_gettol(p1),
|
||||
K_gettol(p2))));
|
||||
default:
|
||||
Z_fatal("fell off switch in _X_cmptokens");
|
||||
return(-1); /* Z_fatal never returns, but i need a this line
|
||||
here to stop lint from complaining */
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
** compare two F_floats using a tolerance
|
||||
*/
|
||||
static int
|
||||
_X_floatdiff(p1,p2,the_tol)
|
||||
F_float p1,p2;
|
||||
T_tol the_tol;
|
||||
{
|
||||
F_float diff, float_tmp;
|
||||
T_tol tol_tmp;
|
||||
|
||||
/*
|
||||
** check for null tolerance list
|
||||
*/
|
||||
if (T_isnull(the_tol))
|
||||
{
|
||||
Z_fatal("_X_floatdiff called with a null tolerance");
|
||||
}
|
||||
|
||||
/*
|
||||
** look for an easy answer. i.e -- check
|
||||
** to see if any of the tolerances are of type T_IGNORE
|
||||
** or if the numbers are too small to exceed an absolute
|
||||
** tolerance.
|
||||
** if so, return immediately
|
||||
*/
|
||||
for(tol_tmp=the_tol; !(T_isnull(tol_tmp)) ;tol_tmp=T_getnext(tol_tmp))
|
||||
{
|
||||
if ((T_IGNORE == T_gettype(tol_tmp)) ||
|
||||
/*
|
||||
** take a look at the exponents before you bother
|
||||
** with the mantissas
|
||||
*/
|
||||
((T_ABSOLUTE == T_gettype(tol_tmp))
|
||||
&& !F_zerofloat(T_getfloat(tol_tmp))
|
||||
&& (F_getexp(p1) <
|
||||
F_getexp(T_getfloat(tol_tmp))-1)
|
||||
&& (F_getexp(p2) <
|
||||
F_getexp(T_getfloat(tol_tmp))-1)))
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** ok, we're going to have to do some arithmetic, so
|
||||
** first find the magnitude of the difference
|
||||
*/
|
||||
if (F_getsign(p1) != F_getsign(p2))
|
||||
{
|
||||
diff = F_floatmagadd(p1,p2);
|
||||
}
|
||||
else
|
||||
{
|
||||
diff = F_floatsub(p1,p2);
|
||||
}
|
||||
|
||||
/*
|
||||
** now check to see if the difference exceeds any tolerance
|
||||
*/
|
||||
for(tol_tmp=the_tol; !(T_isnull(tol_tmp)) ;tol_tmp=T_getnext(tol_tmp))
|
||||
{
|
||||
float_tmp = T_getfloat(tol_tmp);
|
||||
|
||||
if (T_gettype(tol_tmp) == T_ABSOLUTE)
|
||||
{
|
||||
/* do nothing */
|
||||
}
|
||||
else if (T_gettype(tol_tmp) == T_RELATIVE)
|
||||
{
|
||||
if (F_floatcmp(p1,p2) > 0)
|
||||
{
|
||||
float_tmp = F_floatmul(p1, float_tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
float_tmp = F_floatmul(p2, float_tmp);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Z_fatal("bad value for type of tolerance in floatdiff");
|
||||
}
|
||||
/*
|
||||
** if we pass this tolerance, then we're done
|
||||
*/
|
||||
if (F_floatcmp(diff,float_tmp) <= 0)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
/*
|
||||
** all of the tolerances were exceeded
|
||||
*/
|
||||
return(1);
|
||||
}
|
14
utils/Spiff/compare.h
Normal file
14
utils/Spiff/compare.h
Normal file
@ -0,0 +1,14 @@
|
||||
/* Copyright (c) 1988 Bellcore
|
||||
** All Rights Reserved
|
||||
** Permission is granted to copy or use this program, EXCEPT that it
|
||||
** may not be sold for profit, the copyright notice must be reproduced
|
||||
** on copies, and credit should be given to Bellcore where it is due.
|
||||
** BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
|
||||
*/
|
||||
|
||||
#ifndef X_INCLUDED
|
||||
|
||||
extern int X_com();
|
||||
|
||||
#define X_INCLUDED
|
||||
#endif
|
43
utils/Spiff/edit.h
Normal file
43
utils/Spiff/edit.h
Normal file
@ -0,0 +1,43 @@
|
||||
/* Copyright (c) 1988 Bellcore
|
||||
** All Rights Reserved
|
||||
** Permission is granted to copy or use this program, EXCEPT that it
|
||||
** may not be sold for profit, the copyright notice must be reproduced
|
||||
** on copies, and credit should be given to Bellcore where it is due.
|
||||
** BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
|
||||
*/
|
||||
|
||||
/*
|
||||
** the naming information hiding conventions are incompletely implemented
|
||||
** for the edit module. I tried to clean it up once, but kept introducing
|
||||
** nasty (ie. core dump) bugs in the miller code. I give up for now.
|
||||
*/
|
||||
#ifndef E_INCLUDED
|
||||
|
||||
#define E_INSERT 1
|
||||
#define E_DELETE 2
|
||||
|
||||
typedef struct edt {
|
||||
struct edt *link;
|
||||
int op;
|
||||
int line1;
|
||||
int line2;
|
||||
} _E_struct, *E_edit;
|
||||
|
||||
#define E_setop(x,y) ((x)->op = (y))
|
||||
#define E_setl1(x,y) ((x)->line1 = (y))
|
||||
#define E_setl2(x,y) ((x)->line2 = (y))
|
||||
#define E_setnext(x,y) ((x)->link = (y))
|
||||
|
||||
#define E_getop(x) ((x)->op)
|
||||
#define E_getl1(x) ((x)->line1)
|
||||
#define E_getl2(x) ((x)->line2)
|
||||
#define E_getnext(x) ((x)->link)
|
||||
|
||||
#define E_NULL ((E_edit) 0)
|
||||
#define E_edit_alloc() (Z_ALLOC(1,_E_struct))
|
||||
|
||||
#define E_INCLUDED
|
||||
|
||||
#endif
|
||||
|
||||
|
92
utils/Spiff/exact.c
Normal file
92
utils/Spiff/exact.c
Normal file
@ -0,0 +1,92 @@
|
||||
/* Copyright (c) 1988 Bellcore
|
||||
** All Rights Reserved
|
||||
** Permission is granted to copy or use this program, EXCEPT that it
|
||||
** may not be sold for profit, the copyright notice must be reproduced
|
||||
** on copies, and credit should be given to Bellcore where it is due.
|
||||
** BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef lint
|
||||
static char rcsid[]= "$Header$";
|
||||
#endif
|
||||
|
||||
#include "misc.h"
|
||||
#include "edit.h"
|
||||
|
||||
/*
|
||||
** routine to compare each object with its ordinal twin
|
||||
*/
|
||||
E_edit
|
||||
Q_do_exact(size1,size2,max_d,comflags)
|
||||
int size1;
|
||||
int size2;
|
||||
int max_d;
|
||||
int comflags;
|
||||
{
|
||||
int i = 0;
|
||||
int diffcnt = 0;
|
||||
int last = Z_MIN(size1,size2);
|
||||
int next_edit = 0;
|
||||
E_edit last_ptr = E_NULL;
|
||||
int start,tmp;
|
||||
E_edit *script;
|
||||
|
||||
script = Z_ALLOC(max_d+1,E_edit);
|
||||
|
||||
if (size1 != size2)
|
||||
{
|
||||
(void) sprintf(Z_err_buf,"unequal number of tokens, %d and %d respectively\n",size1,size2);
|
||||
Z_complain(Z_err_buf);
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
/*
|
||||
** skip identical objects
|
||||
*/
|
||||
while (i<last && (!X_com(i,i,comflags)))
|
||||
{
|
||||
i++;
|
||||
}
|
||||
start = i;
|
||||
/*
|
||||
** see how many difference we have in a row
|
||||
*/
|
||||
while (i<last && X_com(i,i,comflags))
|
||||
{
|
||||
if ((diffcnt += 2) >= max_d+1)
|
||||
Z_exceed(max_d);
|
||||
i++;
|
||||
}
|
||||
/*
|
||||
** build the list of deletions
|
||||
*/
|
||||
for(tmp=start;tmp<i;tmp++,next_edit++)
|
||||
{
|
||||
script[next_edit] = E_edit_alloc();
|
||||
E_setnext(script[next_edit],last_ptr);
|
||||
last_ptr = script[next_edit];
|
||||
|
||||
E_setop(script[next_edit],E_DELETE);
|
||||
E_setl1(script[next_edit],tmp+1);
|
||||
/* no need to set line2, it is never used */
|
||||
E_setl2(script[next_edit],0);
|
||||
}
|
||||
/*
|
||||
** build the list of insertions
|
||||
*/
|
||||
for(tmp=start;tmp<i;tmp++,next_edit++)
|
||||
{
|
||||
script[next_edit] = E_edit_alloc();
|
||||
E_setnext(script[next_edit],last_ptr);
|
||||
last_ptr = script[next_edit];
|
||||
|
||||
E_setop(script[next_edit],E_INSERT);
|
||||
E_setl1(script[next_edit],i);
|
||||
E_setl2(script[next_edit],tmp+1);
|
||||
}
|
||||
} while (i<last);
|
||||
|
||||
return(last_ptr);
|
||||
}
|
17
utils/Spiff/exact.h
Normal file
17
utils/Spiff/exact.h
Normal file
@ -0,0 +1,17 @@
|
||||
/* Copyright (c) 1988 Bellcore
|
||||
** All Rights Reserved
|
||||
** Permission is granted to copy or use this program, EXCEPT that it
|
||||
** may not be sold for profit, the copyright notice must be reproduced
|
||||
** on copies, and credit should be given to Bellcore where it is due.
|
||||
** BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
|
||||
*/
|
||||
|
||||
#ifndef Q_INCLUDED
|
||||
|
||||
#include "edit.h"
|
||||
|
||||
extern E_edit Q_do_exact();
|
||||
|
||||
#define Q_INCLUDED
|
||||
|
||||
#endif
|
29
utils/Spiff/flagdefs.h
Normal file
29
utils/Spiff/flagdefs.h
Normal file
@ -0,0 +1,29 @@
|
||||
/* Copyright (c) 1988 Bellcore
|
||||
** All Rights Reserved
|
||||
** Permission is granted to copy or use this program, EXCEPT that it
|
||||
** may not be sold for profit, the copyright notice must be reproduced
|
||||
** on copies, and credit should be given to Bellcore where it is due.
|
||||
** BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
|
||||
*/
|
||||
|
||||
/*
|
||||
** flags used by both parser and comparison routines
|
||||
*/
|
||||
#define U_INCLUDE_WS 001
|
||||
|
||||
/*
|
||||
** flags used only by the comparison routines
|
||||
*/
|
||||
#define U_BYTE_COMPARE 002
|
||||
#define U_NO_CASE 004
|
||||
|
||||
/*
|
||||
** flag used by the output routine
|
||||
*/
|
||||
#define U_TOKENS 010
|
||||
|
||||
/*
|
||||
** flags used only by the parser
|
||||
*/
|
||||
#define U_INC_SIGN 020
|
||||
#define U_NEED_DECIMAL 040
|
811
utils/Spiff/float.c
Normal file
811
utils/Spiff/float.c
Normal file
@ -0,0 +1,811 @@
|
||||
/* Copyright (c) 1988 Bellcore
|
||||
** All Rights Reserved
|
||||
** Permission is granted to copy or use this program, EXCEPT that it
|
||||
** may not be sold for profit, the copyright notice must be reproduced
|
||||
** on copies, and credit should be given to Bellcore where it is due.
|
||||
** BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef lint
|
||||
static char rcsid[]= "$Header$";
|
||||
#endif
|
||||
|
||||
#include <ctype.h>
|
||||
#include "misc.h"
|
||||
#include "floatrep.h"
|
||||
#include "float.h"
|
||||
#include "strings.h"
|
||||
|
||||
#define _F_GETEND(x) (x + (strlen(x)-1))
|
||||
|
||||
/*
|
||||
int floatcnt = 0;
|
||||
*/
|
||||
/*
|
||||
** routines to convert strings to our internal floating point form
|
||||
** isfloat just looks at the string
|
||||
** to see if a conversion is reasonable
|
||||
** it does look-ahead on when it sees an 'e' and such.
|
||||
** atocf actually does the conversion.
|
||||
** these two routines could probably be combined
|
||||
*/
|
||||
|
||||
/*
|
||||
** test to see if the string can reasonably
|
||||
** be interpreted as floating point number
|
||||
** returns 0 if string can't be interpreted as a float
|
||||
** otherwise returns the number of digits that will be used in F_atof
|
||||
*/
|
||||
F_isfloat(str,need_decimal,allow_sign)
|
||||
char *str;
|
||||
int need_decimal; /* if non-zero, require that a decimal point be present
|
||||
otherwise, accept strings like "123" */
|
||||
int allow_sign; /* if non-zero, allow + or - to set the sign */
|
||||
{
|
||||
int man_length = 0; /* length of the fractional part (mantissa) */
|
||||
int exp_length = 0; /* length of the exponential part */
|
||||
int got_a_digit = 0; /* flag to set if we ever see a digit */
|
||||
|
||||
/*
|
||||
** look for an optional leading sign marker
|
||||
*/
|
||||
if (allow_sign && ('+' == *str || '-' == *str))
|
||||
{
|
||||
str++; man_length++;
|
||||
}
|
||||
/*
|
||||
** count up the digits on the left hand side
|
||||
** of the decimal point
|
||||
*/
|
||||
while(isdigit(*str))
|
||||
{
|
||||
got_a_digit = 1;
|
||||
str++; man_length++;
|
||||
}
|
||||
|
||||
/*
|
||||
** check for a decimal point
|
||||
*/
|
||||
if ('.' == *str)
|
||||
{
|
||||
str++; man_length++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (need_decimal)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** collect the digits on the right hand
|
||||
** side of the decimal point
|
||||
*/
|
||||
while(isdigit(*str))
|
||||
{
|
||||
got_a_digit = 1;
|
||||
str++; man_length++;
|
||||
}
|
||||
|
||||
if (!got_a_digit)
|
||||
return(0);
|
||||
|
||||
/*
|
||||
** now look ahead for an exponent
|
||||
*/
|
||||
if ('e' == *str ||
|
||||
'E' == *str ||
|
||||
'd' == *str ||
|
||||
'D' == *str)
|
||||
{
|
||||
str++; exp_length++;
|
||||
if ('+' == *str || '-' == *str)
|
||||
{
|
||||
str++; exp_length++;
|
||||
}
|
||||
|
||||
if (!isdigit(*str))
|
||||
{
|
||||
/*
|
||||
** look ahead went too far,
|
||||
** so return just the length of the mantissa
|
||||
*/
|
||||
return(man_length);
|
||||
}
|
||||
|
||||
while (isdigit(*str))
|
||||
{
|
||||
str++; exp_length++;
|
||||
}
|
||||
}
|
||||
return(man_length+exp_length); /* return the total length */
|
||||
}
|
||||
|
||||
/*
|
||||
** routine to convert a string to our internal
|
||||
** floating point representation
|
||||
**
|
||||
** similar to atof()
|
||||
*/
|
||||
F_float
|
||||
F_atof(str,allflag)
|
||||
char *str;
|
||||
int allflag; /* require that exactly all the characters are used */
|
||||
{
|
||||
char *beg = str; /* place holder for beginning of the string */
|
||||
char man[R_MANMAX]; /* temporary location to build the mantissa */
|
||||
int length = 0; /* length of the mantissa so far */
|
||||
int got_a_digit = 0; /* flag to set if we get a non-zero digit */
|
||||
int i;
|
||||
int resexp;
|
||||
|
||||
F_float res; /* where we build the result */
|
||||
|
||||
/*
|
||||
floatcnt++;
|
||||
*/
|
||||
res = R_makefloat();
|
||||
|
||||
R_setsign(res,R_POSITIVE);
|
||||
|
||||
resexp = 0;
|
||||
man[0] = '\0';
|
||||
|
||||
/*
|
||||
** check for leading sign
|
||||
*/
|
||||
if ('+' == *str)
|
||||
{
|
||||
/*
|
||||
** sign should already be positive, see above in this
|
||||
** routine, so just skip the plus sign
|
||||
*/
|
||||
str++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ('-' == *str)
|
||||
{
|
||||
R_setsign(res,R_NEGATIVE);
|
||||
str++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** skip any leading zeros
|
||||
*/
|
||||
while('0' == *str)
|
||||
{
|
||||
str++;
|
||||
}
|
||||
|
||||
/*
|
||||
** now snarf up the digits on the left hand side
|
||||
** of the decimal point
|
||||
*/
|
||||
while(isdigit(*str))
|
||||
{
|
||||
got_a_digit = 1;
|
||||
man[length++] = *str++;
|
||||
man[length] = '\0';
|
||||
resexp++;
|
||||
}
|
||||
|
||||
/*
|
||||
** skip the decimal point if there is one
|
||||
*/
|
||||
if ('.' == *str)
|
||||
str++;
|
||||
|
||||
/*
|
||||
** trim off any leading zeros (on the right hand side)
|
||||
** if there were no digits in front of the decimal point.
|
||||
*/
|
||||
|
||||
if (!got_a_digit)
|
||||
{
|
||||
while('0' == *str)
|
||||
{
|
||||
str++;
|
||||
resexp--;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** now snarf up the digits on the right hand side
|
||||
*/
|
||||
while(isdigit(*str))
|
||||
{
|
||||
man[length++] = *str++;
|
||||
man[length] = '\0';
|
||||
}
|
||||
|
||||
if ('e' == *str ||
|
||||
'E' == *str ||
|
||||
'd' == *str ||
|
||||
'D' == *str )
|
||||
{
|
||||
str++;
|
||||
resexp += atoi(str);
|
||||
}
|
||||
|
||||
if (allflag)
|
||||
{
|
||||
if ('+' == *str ||
|
||||
'-' == *str)
|
||||
{
|
||||
str++;
|
||||
}
|
||||
while (isdigit(*str))
|
||||
{
|
||||
str++;
|
||||
}
|
||||
if ('\0' != *str)
|
||||
{
|
||||
(void) sprintf(Z_err_buf,
|
||||
"didn't use up all of %s in atocf",
|
||||
beg);
|
||||
Z_fatal(Z_err_buf);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** check for special case of all zeros in the mantissa
|
||||
*/
|
||||
for (i=0;i<length;i++)
|
||||
{
|
||||
if (man[i] != '0')
|
||||
{
|
||||
/*
|
||||
** the mantissa is non-zero, so return it unchanged
|
||||
*/
|
||||
S_trimzeros(man);
|
||||
/*
|
||||
** save a copy of the mantissa
|
||||
*/
|
||||
R_setfrac(res,man);
|
||||
R_setexp(res,resexp);
|
||||
return(res);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** the answer is 0, so . . .
|
||||
*/
|
||||
R_setzero(res);
|
||||
return(res);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** add s2 to s1
|
||||
*/
|
||||
static
|
||||
void
|
||||
_F_stradd(s1,s2)
|
||||
char *s1,*s2;
|
||||
{
|
||||
char *end1 = s1 + (strlen(s1)-1);
|
||||
char *end2 = s2 + (strlen(s2)-1);
|
||||
|
||||
static char result[R_MANMAX];
|
||||
char *resptr = result+(R_MANMAX-1); /*point to the end of the array */
|
||||
int carry = 0;
|
||||
int tmp,val1,val2;
|
||||
|
||||
*resptr-- = '\0';
|
||||
|
||||
while ((end1 >= s1) || ( end2 >= s2))
|
||||
{
|
||||
if (end1 >= s1)
|
||||
{
|
||||
val1 = *end1 - '0';
|
||||
--end1;
|
||||
}
|
||||
else
|
||||
{
|
||||
val1 = 0;
|
||||
}
|
||||
|
||||
if (end2 >= s2)
|
||||
{
|
||||
val2 = *end2 - '0';
|
||||
--end2;
|
||||
}
|
||||
else
|
||||
{
|
||||
val2 = 0;
|
||||
}
|
||||
|
||||
tmp = val1 + val2 + carry;
|
||||
if (tmp > 9)
|
||||
{
|
||||
carry = 1;
|
||||
tmp -= 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
carry = 0;
|
||||
}
|
||||
|
||||
*resptr-- = tmp+'0';
|
||||
}
|
||||
if (carry)
|
||||
{
|
||||
*resptr = '1';
|
||||
}
|
||||
else
|
||||
{
|
||||
resptr++;
|
||||
}
|
||||
(void) strcpy(s1,resptr);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
** add zero(s) onto the end of a string
|
||||
*/
|
||||
static void
|
||||
addzeros(ptr,count)
|
||||
char *ptr;
|
||||
int count;
|
||||
{
|
||||
for(;count> 0;count--)
|
||||
{
|
||||
(void) strcat(ptr,"0");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
** subtract two mantissa strings
|
||||
*/
|
||||
F_float
|
||||
F_floatsub(p1,p2)
|
||||
F_float p1,p2;
|
||||
{
|
||||
static F_float result;
|
||||
static needinit = 1;
|
||||
static char man1[R_MANMAX],man2[R_MANMAX],diff[R_MANMAX];
|
||||
int exp1,exp2;
|
||||
char *diffptr,*big,*small;
|
||||
int man_cmp_val,i,borrow;
|
||||
|
||||
if (needinit)
|
||||
{
|
||||
result = R_makefloat();
|
||||
needinit = 0;
|
||||
}
|
||||
|
||||
man1[0] = '\0';
|
||||
man2[0] = '\0';
|
||||
|
||||
exp1 = R_getexp(p1);
|
||||
exp2 = R_getexp(p2);
|
||||
|
||||
/*
|
||||
** line up the mantissas
|
||||
*/
|
||||
while (exp1 < exp2)
|
||||
{
|
||||
(void) strcat(man1,"0");
|
||||
exp1++;
|
||||
}
|
||||
|
||||
while(exp1 > exp2)
|
||||
{
|
||||
(void) strcat(man2,"0");
|
||||
exp2++;
|
||||
}
|
||||
|
||||
if (exp1 != exp2) /* boiler plate assertion */
|
||||
{
|
||||
Z_fatal("mantissas didn't get lined up properly in floatsub");
|
||||
}
|
||||
|
||||
(void) strcat(man1,R_getfrac(p1));
|
||||
(void) strcat(man2,R_getfrac(p2));
|
||||
|
||||
/*
|
||||
** now that the mantissa are aligned,
|
||||
** if the strings are the same, return 0
|
||||
*/
|
||||
if((man_cmp_val = strcmp(man1,man2)) == 0)
|
||||
{
|
||||
R_setzero(result);
|
||||
return(result);
|
||||
}
|
||||
|
||||
/*
|
||||
** pad the shorter string with 0's
|
||||
** when this loop finishes, both mantissas should
|
||||
** have the same length
|
||||
*/
|
||||
if (strlen(man1)> strlen(man2))
|
||||
{
|
||||
addzeros(man2,strlen(man1)-strlen(man2));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strlen(man1)<strlen(man2))
|
||||
{
|
||||
addzeros(man1,strlen(man2)-strlen(man1));
|
||||
}
|
||||
}
|
||||
|
||||
if (strlen(man1) != strlen(man2)) /* pure boilerplate */
|
||||
{
|
||||
Z_fatal("lengths not equal in F_floatsub");
|
||||
}
|
||||
|
||||
if (man_cmp_val < 0)
|
||||
{
|
||||
big = man2;
|
||||
small = man1;
|
||||
}
|
||||
else
|
||||
{
|
||||
big = man1;
|
||||
small = man2;
|
||||
}
|
||||
|
||||
/*
|
||||
** find the difference between the mantissas
|
||||
*/
|
||||
for(i=(strlen(big)-1),borrow=0,diff[strlen(big)] = '\0';i>=0;i--)
|
||||
{
|
||||
char from;
|
||||
if (borrow)
|
||||
{
|
||||
if (big[i] == '0')
|
||||
{
|
||||
from = '9';
|
||||
}
|
||||
else
|
||||
{
|
||||
from = big[i]-1;
|
||||
borrow = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(big[i]<small[i])
|
||||
{
|
||||
from = '9'+1;
|
||||
borrow = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
from = big[i];
|
||||
}
|
||||
}
|
||||
diff[i] = (from-small[i]) + '0';
|
||||
}
|
||||
|
||||
/*
|
||||
** trim the leading zeros on the difference
|
||||
*/
|
||||
diffptr = diff;
|
||||
while('0' == *diffptr)
|
||||
{
|
||||
diffptr++;
|
||||
exp1--;
|
||||
}
|
||||
|
||||
R_setexp(result,exp1); /* exponents are equal at the point */
|
||||
R_setfrac(result,diffptr);
|
||||
R_setsign(result,R_POSITIVE);
|
||||
return(result);
|
||||
}
|
||||
|
||||
F_floatcmp(f1,f2)
|
||||
F_float f1,f2;
|
||||
{
|
||||
static char man1[R_MANMAX],man2[R_MANMAX];
|
||||
|
||||
/*
|
||||
** special case for zero
|
||||
*/
|
||||
if (R_zerofloat(f1))
|
||||
{
|
||||
if (R_zerofloat(f2))
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (R_zerofloat(f2))
|
||||
{
|
||||
return(1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** to reach this point, both numbers must be non zeros
|
||||
*/
|
||||
if (R_getexp(f1) < R_getexp(f2))
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
|
||||
if (R_getexp(f1) > R_getexp(f2))
|
||||
{
|
||||
return(1);
|
||||
}
|
||||
|
||||
(void) strcpy(man1,R_getfrac(f1));
|
||||
S_trimzeros(man1);
|
||||
|
||||
(void) strcpy(man2,R_getfrac(f2));
|
||||
S_trimzeros(man2);
|
||||
return(strcmp(man1,man2));
|
||||
}
|
||||
|
||||
F_float
|
||||
F_floatmul(f1,f2)
|
||||
F_float f1,f2;
|
||||
{
|
||||
static char prod[R_MANMAX];
|
||||
char *end;
|
||||
int count1 = 0;
|
||||
int count2 = 0;
|
||||
int tmp,len;
|
||||
char *end1;
|
||||
char *end2;
|
||||
static char man1[R_MANMAX],man2[R_MANMAX];
|
||||
char *bigman,*smallman;
|
||||
static F_float result;
|
||||
static int needinit = 1;
|
||||
|
||||
if (needinit)
|
||||
{
|
||||
result = R_makefloat();
|
||||
needinit = 0;
|
||||
}
|
||||
/*
|
||||
** special case for a zero result
|
||||
*/
|
||||
if (R_zerofloat(f1) || R_zerofloat(f2))
|
||||
{
|
||||
R_setzero(result);
|
||||
return(result);
|
||||
}
|
||||
|
||||
(void) strcpy(man1,R_getfrac(f1));
|
||||
(void) strcpy(man2,R_getfrac(f2));
|
||||
|
||||
end1 = _F_GETEND(man1);
|
||||
end2 = _F_GETEND(man2);
|
||||
|
||||
/*
|
||||
** decide which number will cause multiplication loop to go
|
||||
** around the least
|
||||
*/
|
||||
while(end1 >= man1)
|
||||
{
|
||||
count1 += *end1 - '0';
|
||||
end1--;
|
||||
}
|
||||
|
||||
while(end2 >= man2)
|
||||
{
|
||||
count2 += *end2 - '0';
|
||||
end2--;
|
||||
}
|
||||
|
||||
|
||||
if (count1 > count2)
|
||||
{
|
||||
bigman = man1;
|
||||
smallman = man2;
|
||||
}
|
||||
else
|
||||
{
|
||||
bigman = man2;
|
||||
smallman = man1;
|
||||
}
|
||||
S_trimzeros(bigman);
|
||||
S_trimzeros(smallman);
|
||||
len = strlen(bigman) + strlen(smallman);
|
||||
|
||||
end = _F_GETEND(smallman);
|
||||
(void) strcpy(prod,"0");
|
||||
|
||||
/*
|
||||
** multiplication by repeated addition
|
||||
*/
|
||||
while(end >= smallman)
|
||||
{
|
||||
for(tmp = 0;tmp<*end-'0';tmp++)
|
||||
{
|
||||
_F_stradd(prod,bigman);
|
||||
}
|
||||
addzeros(bigman,1);
|
||||
end--;
|
||||
}
|
||||
|
||||
R_setfrac(result,prod);
|
||||
R_setexp(result,(((R_getexp(f1) + R_getexp(f2)) - len)+ strlen(prod)));
|
||||
|
||||
if (R_getsign(f1) == R_getsign(f2))
|
||||
{
|
||||
R_setsign(result,R_POSITIVE);
|
||||
}
|
||||
else
|
||||
{
|
||||
R_setsign(result,R_NEGATIVE);
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
_F_xor(x,y)
|
||||
{
|
||||
return(((x) && !(y)) || (!(x) && (y)));
|
||||
}
|
||||
#define _F_SAMESIGN(x,y) _F_xor((x<0),(y<0))
|
||||
#define _F_ABSADD(x,y) (Z_ABS(x) + Z_ABS(y))
|
||||
|
||||
_F_ABSDIFF(x,y)
|
||||
{
|
||||
if (Z_ABS(x) < Z_ABS(y))
|
||||
{
|
||||
return(Z_ABS(y) - Z_ABS(x));
|
||||
}
|
||||
else
|
||||
{
|
||||
return(Z_ABS(x) - Z_ABS(y));
|
||||
}
|
||||
}
|
||||
/*
|
||||
** add two floats without regard to sign
|
||||
*/
|
||||
F_float
|
||||
F_floatmagadd(p1,p2)
|
||||
F_float p1,p2;
|
||||
{
|
||||
static F_float result;
|
||||
static int needinit = 1;
|
||||
|
||||
static char man1[R_MANMAX],man2[R_MANMAX];
|
||||
|
||||
int digits; /* count of the number of digits needed to represent the
|
||||
result */
|
||||
int resexp; /* exponent of the result */
|
||||
int len; /* length of the elements before adding */
|
||||
char *diffptr;
|
||||
|
||||
if (needinit)
|
||||
{
|
||||
result = R_makefloat();
|
||||
needinit = 0;
|
||||
}
|
||||
(void) strcpy(man1,"");
|
||||
(void) strcpy(man2,"");
|
||||
|
||||
/*
|
||||
** find the difference in the exponents number of digits
|
||||
*/
|
||||
if( _F_SAMESIGN(R_getexp(p1),R_getexp(p2)))
|
||||
{
|
||||
digits = _F_ABSDIFF(R_getexp(p1),R_getexp(p2));
|
||||
}
|
||||
else
|
||||
{
|
||||
digits = _F_ABSADD(R_getexp(p1),R_getexp(p2));
|
||||
}
|
||||
|
||||
/*
|
||||
** make sure that there is room to store the result
|
||||
*/
|
||||
if (digits>0)
|
||||
{
|
||||
if (R_getexp(p1) < R_getexp(p2))
|
||||
{
|
||||
/*
|
||||
** leave room for terminator
|
||||
*/
|
||||
if (digits+strlen(R_getfrac(p1)) > (R_MANMAX-1))
|
||||
{
|
||||
(void) sprintf(Z_err_buf,
|
||||
"numbers differ by too much in magnitude");
|
||||
Z_fatal(Z_err_buf);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
** leave room for terminator
|
||||
*/
|
||||
if (digits+strlen(R_getfrac(p2)) > (R_MANMAX-1))
|
||||
{
|
||||
(void) sprintf(Z_err_buf,
|
||||
"numbers differ by too much in magnitude");
|
||||
Z_fatal(Z_err_buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
** leave room for terminator and possible carry
|
||||
*/
|
||||
if (Z_MAX(strlen(R_getfrac(p1)),
|
||||
strlen(R_getfrac(p2))) > (R_MANMAX-2))
|
||||
{
|
||||
(void) sprintf(Z_err_buf,
|
||||
"numbers differ by too much in magnitude");
|
||||
Z_fatal(Z_err_buf);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** pad zeroes on the front of the smaller number
|
||||
*/
|
||||
if (R_getexp(p1) < R_getexp(p2))
|
||||
{
|
||||
|
||||
addzeros(man1,digits);
|
||||
resexp = R_getexp(p2);
|
||||
}
|
||||
else
|
||||
{
|
||||
addzeros(man2,digits);
|
||||
resexp = R_getexp(p1);
|
||||
}
|
||||
(void) strcat(man1,R_getfrac(p1));
|
||||
(void) strcat(man2,R_getfrac(p2));
|
||||
|
||||
len = Z_MAX(strlen(man1),strlen(man2));
|
||||
|
||||
/*
|
||||
** add the two values
|
||||
*/
|
||||
_F_stradd(man1,man2);
|
||||
|
||||
/*
|
||||
** adjust the exponent to account for a
|
||||
** possible carry
|
||||
*/
|
||||
resexp += strlen(man1) - len;
|
||||
|
||||
|
||||
/*
|
||||
** trim the leading zeros on the sum
|
||||
*/
|
||||
diffptr = man1;
|
||||
while('0' == *diffptr)
|
||||
{
|
||||
diffptr++;
|
||||
resexp--;
|
||||
}
|
||||
|
||||
R_setfrac(result,diffptr);
|
||||
R_setexp(result,resexp);
|
||||
R_setsign(result,R_POSITIVE);
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
/*
|
||||
** useful debugging routine. we don't call it in the release,
|
||||
** so it is commented out, but we'll leave it for future use
|
||||
*/
|
||||
|
||||
/*
|
||||
F_printfloat(fl)
|
||||
F_float fl;
|
||||
{
|
||||
(void) printf("fraction = :%s: exp = %d sign = %c\n",
|
||||
R_getfrac(fl),
|
||||
R_getexp(fl),
|
||||
((R_getsign(fl) == R_POSITIVE) ? '+': '-'));
|
||||
|
||||
}
|
||||
*/
|
35
utils/Spiff/float.h
Normal file
35
utils/Spiff/float.h
Normal file
@ -0,0 +1,35 @@
|
||||
/* Copyright (c) 1988 Bellcore
|
||||
** All Rights Reserved
|
||||
** Permission is granted to copy or use this program, EXCEPT that it
|
||||
** may not be sold for profit, the copyright notice must be reproduced
|
||||
** on copies, and credit should be given to Bellcore where it is due.
|
||||
** BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
|
||||
*/
|
||||
|
||||
|
||||
#include "floatrep.h"
|
||||
|
||||
#ifndef F_INCLUDED
|
||||
|
||||
/*
|
||||
** flags for F_atof
|
||||
*/
|
||||
#define NO_USE_ALL 0
|
||||
#define USE_ALL 1
|
||||
|
||||
typedef struct R_flstr *F_float;
|
||||
#define F_getexp(x) R_getexp(x)
|
||||
#define F_getsign(x) R_getsign(x)
|
||||
#define F_zerofloat(x) R_zerofloat(x)
|
||||
|
||||
extern F_float F_atof();
|
||||
|
||||
extern F_float F_floatmul();
|
||||
extern F_float F_floatmagadd();
|
||||
extern F_float F_floatsub();
|
||||
|
||||
#define F_null ((F_float) 0)
|
||||
|
||||
#define F_INCLUDED
|
||||
|
||||
#endif
|
32
utils/Spiff/floatrep.c
Normal file
32
utils/Spiff/floatrep.c
Normal file
@ -0,0 +1,32 @@
|
||||
/* Copyright (c) 1988 Bellcore
|
||||
** All Rights Reserved
|
||||
** Permission is granted to copy or use this program, EXCEPT that it
|
||||
** may not be sold for profit, the copyright notice must be reproduced
|
||||
** on copies, and credit should be given to Bellcore where it is due.
|
||||
** BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef lint
|
||||
static char rcsid[]= "$Header$";
|
||||
#endif
|
||||
|
||||
#include "misc.h"
|
||||
#include "floatrep.h"
|
||||
|
||||
R_float
|
||||
R_makefloat()
|
||||
{
|
||||
R_float retval;
|
||||
|
||||
retval = Z_ALLOC(1,struct R_flstr);
|
||||
retval->mantissa = Z_ALLOC(R_MANMAX,char);
|
||||
return(retval);
|
||||
}
|
||||
|
||||
R_getexp(ptr)
|
||||
R_float ptr;
|
||||
{
|
||||
return(ptr->exponent);
|
||||
}
|
||||
|
66
utils/Spiff/floatrep.h
Normal file
66
utils/Spiff/floatrep.h
Normal file
@ -0,0 +1,66 @@
|
||||
/* Copyright (c) 1988 Bellcore
|
||||
** All Rights Reserved
|
||||
** Permission is granted to copy or use this program, EXCEPT that it
|
||||
** may not be sold for profit, the copyright notice must be reproduced
|
||||
** on copies, and credit should be given to Bellcore where it is due.
|
||||
** BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
|
||||
*/
|
||||
|
||||
/*
|
||||
** header file that defines canonical floating point structure
|
||||
** and routines
|
||||
*/
|
||||
|
||||
|
||||
#ifndef R_INCLUDED
|
||||
|
||||
/*
|
||||
** when evaluated to a string, the fractional part will
|
||||
** not exceed this length
|
||||
*/
|
||||
#define R_MANMAX 200
|
||||
|
||||
#define R_POSITIVE 0
|
||||
#define R_NEGATIVE 1
|
||||
|
||||
struct R_flstr {
|
||||
int exponent;
|
||||
int man_sign;
|
||||
char *mantissa;
|
||||
};
|
||||
|
||||
typedef struct R_flstr *R_float;
|
||||
|
||||
#define R_getfrac(x) (x->mantissa)
|
||||
|
||||
extern R_float R_makefloat();
|
||||
|
||||
extern int R_getexp();
|
||||
|
||||
#define R_getsign(x) (x->man_sign)
|
||||
|
||||
/*
|
||||
** takes a string
|
||||
*/
|
||||
#define R_setfrac(x,y) ((void)strcpy(x->mantissa,y))
|
||||
/*
|
||||
** takes an int
|
||||
*/
|
||||
#define R_setexp(x,y) (x->exponent = y)
|
||||
/*
|
||||
** takes a sign
|
||||
*/
|
||||
#define R_setsign(x,y) (x->man_sign = y)
|
||||
|
||||
/*
|
||||
#define R_incexp(x) ((x->exponent)++)
|
||||
#define R_decexp(x) ((x->exponent)--)
|
||||
*/
|
||||
|
||||
#define R_setzero(x) R_setfrac(x,"0");R_setexp(x,0);R_setsign(x,R_POSITIVE)
|
||||
|
||||
#define R_zerofloat(x) ((0 == x->exponent) && (!strcmp(x->mantissa,"0")))
|
||||
|
||||
#define R_INCLUDED
|
||||
|
||||
#endif
|
169
utils/Spiff/line.c
Normal file
169
utils/Spiff/line.c
Normal file
@ -0,0 +1,169 @@
|
||||
/* Copyright (c) 1988 Bellcore
|
||||
** All Rights Reserved
|
||||
** Permission is granted to copy or use this program, EXCEPT that it
|
||||
** may not be sold for profit, the copyright notice must be reproduced
|
||||
** on copies, and credit should be given to Bellcore where it is due.
|
||||
** BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef lint
|
||||
static char rcsid[]= "$Header$";
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include "misc.h"
|
||||
#include "token.h"
|
||||
#include "line.h"
|
||||
|
||||
char *_L_al[_L_MAXLINES]; /* storage for lines */
|
||||
char *_L_bl[_L_MAXLINES];
|
||||
|
||||
int _L_ai[_L_MAXLINES]; /* index from token line number to first token */
|
||||
int _L_bi[_L_MAXLINES];
|
||||
|
||||
int _L_ac[_L_MAXLINES]; /* count of tokens on this token line */
|
||||
int _L_bc[_L_MAXLINES];
|
||||
|
||||
int _L_arlm; /* count of real lines in the file */
|
||||
int _L_brlm;
|
||||
|
||||
int _L_aclm; /* count of content lines in the file */
|
||||
int _L_bclm;
|
||||
|
||||
int _L_atlm; /* count of token lines in the file */
|
||||
int _L_btlm;
|
||||
|
||||
int _L_aclindex[_L_MAXLINES]; /* mapping from content lines to real lines*/
|
||||
int _L_bclindex[_L_MAXLINES];
|
||||
|
||||
int _L_atlindex[_L_MAXLINES]; /*mapping from token lines to content lines */
|
||||
int _L_btlindex[_L_MAXLINES];
|
||||
|
||||
|
||||
static void
|
||||
_L_setrline(file,X,str)
|
||||
int file;
|
||||
int X;
|
||||
char *str;
|
||||
{
|
||||
if (file)
|
||||
{
|
||||
S_savestr(&_L_bl[X],str);
|
||||
}
|
||||
else
|
||||
{
|
||||
S_savestr(&_L_al[X],str);
|
||||
}
|
||||
return;
|
||||
}
|
||||
/*
|
||||
** returns 1 if we reached the end of file
|
||||
** returns 0 if there is more to do
|
||||
**
|
||||
** stores data and sets maximum counts
|
||||
*/
|
||||
L_init_file(fnumber,fname)
|
||||
int fnumber;
|
||||
char *fname;
|
||||
{
|
||||
extern char *fgets();
|
||||
FILE *fp;
|
||||
static char buf[Z_LINELEN+2]; /* +2 is to leave room for us to add
|
||||
a newline if we need to */
|
||||
int ret_val = 1;
|
||||
int tmplen;
|
||||
|
||||
if ((fp = fopen(fname,"r")) == (FILE*) NULL)
|
||||
{
|
||||
(void) sprintf(Z_err_buf, "Cannot open file %s.\n",fname);
|
||||
Z_fatal(Z_err_buf);
|
||||
}
|
||||
|
||||
/*
|
||||
** clear the line count
|
||||
*/
|
||||
_L_setrlmx(fnumber,0);
|
||||
|
||||
/*
|
||||
** read in the entire file
|
||||
*/
|
||||
while (fgets(buf,Z_LINELEN+1,fp) != (char *) NULL)
|
||||
{
|
||||
tmplen = strlen(buf);
|
||||
if (tmplen <= 0)
|
||||
{
|
||||
(void) sprintf(Z_err_buf,
|
||||
"fatal error -- got 0 length line %d in file %s\n",
|
||||
L_getrlmax(fnumber)+1,
|
||||
fname);
|
||||
Z_fatal(Z_err_buf);
|
||||
}
|
||||
else if (tmplen > Z_LINELEN)
|
||||
{
|
||||
(void) sprintf(Z_err_buf,
|
||||
"got fatally long line %d in file %s length is %d, must be a bug\n",
|
||||
L_getrlmax(fnumber)+1,
|
||||
fname,tmplen);
|
||||
Z_fatal(Z_err_buf);
|
||||
}
|
||||
/*
|
||||
** look for newline as last character
|
||||
*/
|
||||
if ('\n' != buf[tmplen-1])
|
||||
{
|
||||
/*
|
||||
** did we run out room in the buffer?
|
||||
*/
|
||||
if (tmplen == Z_LINELEN)
|
||||
{
|
||||
(void) sprintf(Z_err_buf,
|
||||
"line %d too long in file %s, newline added after %d characters\n",
|
||||
L_getrlmax(fnumber)+1,
|
||||
fname,Z_LINELEN);
|
||||
Z_complain(Z_err_buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
(void) sprintf(Z_err_buf,
|
||||
"didn't find a newline at end of line %d in file %s, added one\n",
|
||||
L_getrlmax(fnumber)+1,
|
||||
fname);
|
||||
Z_complain(Z_err_buf);
|
||||
}
|
||||
|
||||
buf[tmplen] = '\n';
|
||||
buf[tmplen+1] = '\0';
|
||||
}
|
||||
|
||||
_L_setrline(fnumber,L_getrlmax(fnumber),buf);
|
||||
|
||||
if (L_getrlmax(fnumber) >= _L_MAXLINES-1)
|
||||
{
|
||||
(void) sprintf(Z_err_buf,
|
||||
"warning -- ran out of space reading %s, truncated to %d lines\n",
|
||||
fname,_L_MAXLINES);
|
||||
Z_complain(Z_err_buf);
|
||||
ret_val= 0;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
** increment the line count
|
||||
*/
|
||||
_L_incrlmx(fnumber);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
(void) fclose(fp);
|
||||
/*
|
||||
** reset line numbers
|
||||
*/
|
||||
L_setclmax(fnumber,0);
|
||||
L_settlmax(fnumber,0);
|
||||
|
||||
return(ret_val);
|
||||
}
|
||||
|
113
utils/Spiff/line.h
Normal file
113
utils/Spiff/line.h
Normal file
@ -0,0 +1,113 @@
|
||||
/* Copyright (c) 1988 Bellcore
|
||||
** All Rights Reserved
|
||||
** Permission is granted to copy or use this program, EXCEPT that it
|
||||
** may not be sold for profit, the copyright notice must be reproduced
|
||||
** on copies, and credit should be given to Bellcore where it is due.
|
||||
** BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef L_INCLUDED
|
||||
|
||||
#define _L_MAXLINES 10000
|
||||
|
||||
/*
|
||||
** oh god, is this an ugly implementation.
|
||||
** I really should have a two dimensional array of structures
|
||||
** the history of the current arrangement is too long
|
||||
** and ugly to record here.
|
||||
** Someday when I have too much time on my hands . . .
|
||||
*/
|
||||
|
||||
extern char *_L_al[]; /* storage for text in first file */
|
||||
extern char *_L_bl[]; /* storage for text in second file */
|
||||
|
||||
extern int _L_ai[]; /* pointer from token line to first token */
|
||||
extern int _L_bi[];
|
||||
|
||||
extern int _L_ac[]; /* number of tokens on a given token line */
|
||||
extern int _L_bc[];
|
||||
|
||||
extern int _L_aclindex[]; /* mapping from content lines to real lines */
|
||||
extern int _L_bclindex[];
|
||||
|
||||
extern int _L_atlindex[]; /* mapping from lines with tokens to content lines */
|
||||
extern int _L_btlindex[];
|
||||
|
||||
extern int _L_arlm; /* count of real lines */
|
||||
extern int _L_brlm;
|
||||
|
||||
extern int _L_aclm; /* count of content lines */
|
||||
extern int _L_bclm;
|
||||
|
||||
extern int _L_atlm; /* count of lines with tokens */
|
||||
extern int _L_btlm;
|
||||
|
||||
/*
|
||||
** routines to set up mappings from token lines to content lines
|
||||
** and from content lines to real lines
|
||||
*/
|
||||
#define L_setclindex(file,content,real) (file?(_L_bclindex[content]=real):\
|
||||
(_L_aclindex[content]=real))
|
||||
|
||||
#define L_settlindex(file,token,content) (file?(_L_btlindex[token]=content):\
|
||||
(_L_atlindex[token]=content))
|
||||
/*
|
||||
** get line number X from file
|
||||
*/
|
||||
#define L_getrline(file, X) (file?(_L_bl[X]):(_L_al[X]))
|
||||
#define L_getcline(file, X) (file?(_L_bl[_L_bclindex[X]]):\
|
||||
(_L_al[_L_aclindex[X]]))
|
||||
#define L_gettline(file, X) (file?(_L_bl[_L_bclindex[_L_btlindex[X]]]):\
|
||||
(_L_al[_L_aclindex[_L_atlindex[X]]]))
|
||||
|
||||
#define L_cl2rl(file, X) (file?(_L_bclindex[X]):\
|
||||
(_L_aclindex[X]))
|
||||
#define L_tl2cl(file, X) (file?(_L_btlindex[X]):\
|
||||
(_L_atlindex[X]))
|
||||
#define L_tl2rl(file, X) (file?(_L_bclindex[_L_btlindex[X]]):\
|
||||
(_L_aclindex[_L_atlindex[X]]))
|
||||
|
||||
/*
|
||||
** get number of first token on line X of the file
|
||||
*/
|
||||
#define L_getindex(file,X) (file?(_L_bi[X]):(_L_ai[X]))
|
||||
|
||||
/*
|
||||
** get count of number of tokens on line X of first file
|
||||
*/
|
||||
#define L_getcount(file,X) (file?(_L_bc[X]):(_L_ac[X]))
|
||||
|
||||
/*
|
||||
** save number of first token for line X of file
|
||||
*/
|
||||
#define L_setindex(file,index,value) (file?(_L_bi[index]=value):(_L_ai[index]=value))
|
||||
/*
|
||||
** save count of tokens on line X of file
|
||||
*/
|
||||
#define L_setcount(file,index,value) (file?(_L_bc[index]=value):(_L_ac[index]=value))
|
||||
#define L_inccount(file,index) (file?(_L_bc[index]++):(_L_ac[index]++))
|
||||
|
||||
/*
|
||||
** retrieve line and token counts
|
||||
*/
|
||||
#define L_getrlmax(file) (file?_L_brlm:_L_arlm)
|
||||
#define L_getclmax(file) (file?_L_bclm:_L_aclm)
|
||||
#define L_gettlmax(file) (file?_L_btlm:_L_atlm)
|
||||
|
||||
/*
|
||||
** set line and token counts
|
||||
*/
|
||||
#define _L_setrlmx(file,value) (file?(_L_brlm=(value)):(_L_arlm=(value)))
|
||||
#define L_setclmax(file,value) (file?(_L_bclm=(value)):(_L_aclm=(value)))
|
||||
#define L_settlmax(file,value) (file?(_L_btlm=(value)):(_L_atlm=(value)))
|
||||
|
||||
/*
|
||||
** increment line and token counts
|
||||
*/
|
||||
#define _L_incrlmx(file) (file?(_L_brlm++):(_L_arlm++))
|
||||
#define L_incclmax(file) (file?(_L_bclm++):(_L_aclm++))
|
||||
#define L_inctlmax(file) (file?(_L_btlm++):(_L_atlm++))
|
||||
|
||||
#define L_INCLUDED
|
||||
#endif
|
127
utils/Spiff/miller.c
Normal file
127
utils/Spiff/miller.c
Normal file
@ -0,0 +1,127 @@
|
||||
/* Copyright (c) 1988 Bellcore
|
||||
** All Rights Reserved
|
||||
** Permission is granted to copy or use this program, EXCEPT that it
|
||||
** may not be sold for profit, the copyright notice must be reproduced
|
||||
** on copies, and credit should be given to Bellcore where it is due.
|
||||
** BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef lint
|
||||
static char rcsid[]= "$Header$";
|
||||
#endif
|
||||
|
||||
#include "misc.h"
|
||||
#include "token.h"
|
||||
#include "edit.h"
|
||||
|
||||
#define MAXT K_MAXTOKENS
|
||||
#define ORIGIN (max_obj/2)
|
||||
|
||||
#define MILLER_CHATTER 100
|
||||
|
||||
/*
|
||||
** totally opaque miller/myers code
|
||||
** hacked from a version provided by the author
|
||||
*/
|
||||
|
||||
|
||||
E_edit
|
||||
G_do_miller(m,n,max_d,comflags)
|
||||
int m;
|
||||
int n;
|
||||
int max_d;
|
||||
int comflags;
|
||||
{
|
||||
int max_obj = m + n;
|
||||
int
|
||||
lower,
|
||||
upper,
|
||||
d,
|
||||
k,
|
||||
row,
|
||||
col;
|
||||
E_edit new;
|
||||
|
||||
#ifdef STATIC_MEM
|
||||
static E_edit script[MAXT+1];
|
||||
static int last_d[MAXT+1];
|
||||
#else
|
||||
E_edit *script;
|
||||
int *last_d;
|
||||
/*
|
||||
** make space for the two big arrays
|
||||
** these could probably be smaller if I
|
||||
** understood this algorithm at all
|
||||
** as is, i just shoe horned it into my program.
|
||||
** be sure to allocate max_obj + 1 objects as was done
|
||||
** in original miller/myers code
|
||||
*/
|
||||
script = Z_ALLOC(max_obj+1,E_edit);
|
||||
last_d = Z_ALLOC(max_obj+1,int);
|
||||
|
||||
#endif
|
||||
for (row=0;row < m && row < n && X_com(row,row,comflags) == 0; ++row)
|
||||
;
|
||||
last_d[ORIGIN] = row;
|
||||
script[ORIGIN] = E_NULL;
|
||||
lower = (row == m) ? ORIGIN+1 : ORIGIN - 1;
|
||||
upper = (row == n) ? ORIGIN-1 : ORIGIN + 1;
|
||||
if (lower > upper)
|
||||
{
|
||||
/*
|
||||
** the files are identical
|
||||
*/
|
||||
return(E_NULL);
|
||||
}
|
||||
for (d = 1; d <= max_d; ++d) {
|
||||
for (k = lower; k<= upper; k+= 2) {
|
||||
new = E_edit_alloc();
|
||||
|
||||
if (k == ORIGIN-d || k!= ORIGIN+d && last_d[k+1] >= last_d[k-1]) {
|
||||
row = last_d[k+1]+1;
|
||||
E_setnext(new,script[k+1]);
|
||||
E_setop(new,E_DELETE);
|
||||
} else {
|
||||
row = last_d[k-1];
|
||||
E_setnext(new,script[k-1]);
|
||||
E_setop(new,E_INSERT);
|
||||
}
|
||||
|
||||
E_setl1(new,row);
|
||||
col = row + k - ORIGIN;
|
||||
E_setl2(new,col);
|
||||
script[k] = new;
|
||||
|
||||
while (row < m && col < n && X_com(row,col,comflags) == 0) {
|
||||
++row;
|
||||
++col;
|
||||
}
|
||||
last_d[k] = row;
|
||||
if (row == m && col == n) {
|
||||
return(script[k]);
|
||||
}
|
||||
if (row == m)
|
||||
lower = k+2;
|
||||
if (col == n)
|
||||
upper = k-2;
|
||||
}
|
||||
--lower;
|
||||
++upper;
|
||||
#ifndef NOCHATTER
|
||||
if ((d > 0) && (0 == (d % MILLER_CHATTER)))
|
||||
{
|
||||
(void) sprintf(Z_err_buf,
|
||||
"found %d differences\n",
|
||||
d);
|
||||
Z_chatter(Z_err_buf);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
Z_exceed(max_d);
|
||||
/*
|
||||
** dummy lines to shut up lint
|
||||
*/
|
||||
Z_fatal("fell off end of do_miller\n");
|
||||
return(E_NULL);
|
||||
}
|
17
utils/Spiff/miller.h
Normal file
17
utils/Spiff/miller.h
Normal file
@ -0,0 +1,17 @@
|
||||
/* Copyright (c) 1988 Bellcore
|
||||
** All Rights Reserved
|
||||
** Permission is granted to copy or use this program, EXCEPT that it
|
||||
** may not be sold for profit, the copyright notice must be reproduced
|
||||
** on copies, and credit should be given to Bellcore where it is due.
|
||||
** BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
|
||||
*/
|
||||
|
||||
#ifndef G_INCLUDED
|
||||
|
||||
#include "edit.h"
|
||||
|
||||
extern E_edit G_do_miller();
|
||||
|
||||
#define G_INCLUDED
|
||||
|
||||
#endif
|
119
utils/Spiff/misc.c
Normal file
119
utils/Spiff/misc.c
Normal file
@ -0,0 +1,119 @@
|
||||
/* Copyright (c) 1988 Bellcore
|
||||
** All Rights Reserved
|
||||
** Permission is granted to copy or use this program, EXCEPT that it
|
||||
** may not be sold for profit, the copyright notice must be reproduced
|
||||
** on copies, and credit should be given to Bellcore where it is due.
|
||||
** BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef lint
|
||||
static char rcsid[]= "$Header$";
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include "misc.h"
|
||||
#include "visual.h"
|
||||
#include "output.h"
|
||||
|
||||
/*
|
||||
** various routines used throughout the program
|
||||
*/
|
||||
|
||||
static int _Z_qflag = 0;
|
||||
|
||||
void
|
||||
Z_setquiet()
|
||||
{
|
||||
_Z_qflag = 1;
|
||||
}
|
||||
|
||||
char Z_err_buf[Z_LINELEN];
|
||||
|
||||
#ifndef NOCHATTER
|
||||
/*
|
||||
** I/O coverup to reassure users with HUGE files
|
||||
** that spiff is doing something
|
||||
*/
|
||||
void
|
||||
Z_chatter(str)
|
||||
char *str;
|
||||
{
|
||||
if (!_Z_qflag)
|
||||
{
|
||||
(void) fputs("spiff -- ",stderr);
|
||||
(void) fputs(str,stderr);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** complain unless you've been told to be quiet
|
||||
*/
|
||||
void
|
||||
Z_complain(str)
|
||||
char *str;
|
||||
{
|
||||
if (!_Z_qflag)
|
||||
(void) fputs(str,stderr);
|
||||
}
|
||||
|
||||
/*
|
||||
** quit with an error code
|
||||
*/
|
||||
static void
|
||||
_Z_errexit()
|
||||
{
|
||||
(void) exit(2);
|
||||
}
|
||||
|
||||
/*
|
||||
** complain and die
|
||||
*/
|
||||
void
|
||||
_Z_qfatal(str)
|
||||
char *str;
|
||||
{
|
||||
V_cleanup(); /* try reset the device to normal */
|
||||
O_cleanup(); /* " " " " " " */
|
||||
Z_complain(str);
|
||||
_Z_errexit();
|
||||
}
|
||||
|
||||
/*
|
||||
** scream and die
|
||||
*/
|
||||
void
|
||||
Z_fatal(str)
|
||||
char *str;
|
||||
{
|
||||
V_cleanup(); /* try reset the device to normal */
|
||||
O_cleanup(); /* " " " " " " */
|
||||
(void) fputs(str,stderr);
|
||||
_Z_errexit();
|
||||
}
|
||||
|
||||
/*
|
||||
** allocate memory with error checking
|
||||
*/
|
||||
int*
|
||||
_Z_myalloc(k)
|
||||
int k;
|
||||
{
|
||||
int *tmp;
|
||||
if (tmp = (int*) calloc((unsigned)k,(unsigned)1))
|
||||
{
|
||||
return(tmp);
|
||||
}
|
||||
Z_fatal("Out of Memory\n");
|
||||
return(tmp); /* boilerplate to shut up lint */
|
||||
}
|
||||
|
||||
void
|
||||
Z_exceed(d)
|
||||
int d;
|
||||
{
|
||||
(void) sprintf(Z_err_buf,
|
||||
"The files differ in more than %d places\n", d);
|
||||
_Z_qfatal(Z_err_buf);
|
||||
}
|
49
utils/Spiff/misc.h
Normal file
49
utils/Spiff/misc.h
Normal file
@ -0,0 +1,49 @@
|
||||
/* Copyright (c) 1988 Bellcore
|
||||
** All Rights Reserved
|
||||
** Permission is granted to copy or use this program, EXCEPT that it
|
||||
** may not be sold for profit, the copyright notice must be reproduced
|
||||
** on copies, and credit should be given to Bellcore where it is due.
|
||||
** BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
|
||||
*/
|
||||
|
||||
#ifndef Z_INCLUDED
|
||||
|
||||
/*
|
||||
** make sure that if we have a XENIX system, that
|
||||
** we also treat it as an AT and T derivative
|
||||
*/
|
||||
#ifdef XENIX
|
||||
#ifndef ATT
|
||||
#define ATT
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define Z_LINELEN 1024
|
||||
#define Z_WORDLEN 20
|
||||
|
||||
extern char Z_err_buf[];
|
||||
|
||||
/*
|
||||
** helpful macros
|
||||
*/
|
||||
#define Z_ABS(x) (( (x) < (0) )? (-(x)):(x))
|
||||
#define Z_MIN(x,y) (( (x) < (y) )? (x):(y))
|
||||
#define Z_MAX(x,y) (( (x) > (y) )? (x):(y))
|
||||
|
||||
#define Z_ALLOC(n,type) ((type*) _Z_myalloc((n) * sizeof (type)))
|
||||
extern int *_Z_myalloc();
|
||||
|
||||
/*
|
||||
** lines needed to shut up lint
|
||||
*/
|
||||
|
||||
extern void Z_complain();
|
||||
extern void Z_fatal();
|
||||
extern void Z_exceed();
|
||||
extern void Z_setquiet();
|
||||
#ifndef NOCHATTER
|
||||
extern void Z_chatter();
|
||||
#endif
|
||||
|
||||
#define Z_INCLUDED
|
||||
#endif
|
558
utils/Spiff/output.c
Normal file
558
utils/Spiff/output.c
Normal file
@ -0,0 +1,558 @@
|
||||
/* Copyright (c) 1988 Bellcore
|
||||
** All Rights Reserved
|
||||
** Permission is granted to copy or use this program, EXCEPT that it
|
||||
** may not be sold for profit, the copyright notice must be reproduced
|
||||
** on copies, and credit should be given to Bellcore where it is due.
|
||||
** BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef lint
|
||||
static char rcsid[]= "$Header$";
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef M_TERMINFO
|
||||
#include <curses.h>
|
||||
#include <term.h>
|
||||
#endif
|
||||
|
||||
#ifdef M_TERMCAP
|
||||
#ifdef XENIX
|
||||
#include <tcap.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "misc.h"
|
||||
#include "flagdefs.h"
|
||||
#include "edit.h"
|
||||
#include "line.h"
|
||||
#include "token.h"
|
||||
|
||||
static int _O_need_init = 1;
|
||||
static int _O_st_ok = 0;
|
||||
static int _O_doing_ul = 0;
|
||||
static char *_O_st_tmp;
|
||||
#ifdef M_TERMCAP
|
||||
static char _O_startline[Z_WORDLEN];
|
||||
static char _O_endline[Z_WORDLEN];
|
||||
#endif
|
||||
|
||||
static void
|
||||
_O_st_init()
|
||||
{
|
||||
char termn[Z_WORDLEN];
|
||||
#ifdef M_TERMCAP
|
||||
static char entry[1024];
|
||||
#endif
|
||||
|
||||
/*
|
||||
** see if standard out is a terminal
|
||||
*/
|
||||
if (!isatty(1))
|
||||
{
|
||||
_O_need_init = 0;
|
||||
_O_st_ok = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (NULL == (_O_st_tmp = (char*) getenv("TERM")))
|
||||
{
|
||||
Z_complain("can't find TERM entry in environment\n");
|
||||
_O_need_init = 0;
|
||||
_O_st_ok = 0;
|
||||
return;
|
||||
}
|
||||
(void) strcpy(termn,_O_st_tmp);
|
||||
|
||||
#ifdef M_TERMCAP
|
||||
if (1 != tgetent(entry,termn))
|
||||
{
|
||||
Z_complain("can't get TERMCAP info for terminal\n");
|
||||
_O_need_init = 0;
|
||||
_O_st_ok = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
_O_st_tmp = _O_startline;
|
||||
_O_startline[0] = '\0';
|
||||
tgetstr("so",&_O_st_tmp);
|
||||
|
||||
_O_st_tmp = _O_endline;
|
||||
_O_endline[0] = '\0';
|
||||
tgetstr("se",&_O_st_tmp);
|
||||
|
||||
_O_st_ok = (strlen(_O_startline) > 0) && (strlen(_O_endline) > 0);
|
||||
#endif
|
||||
|
||||
#ifdef M_TERMINFO
|
||||
setupterm(termn,1,&_O_st_ok);
|
||||
#endif
|
||||
_O_need_init = 0;
|
||||
}
|
||||
|
||||
void
|
||||
O_cleanup()
|
||||
{
|
||||
/*
|
||||
** this probably isn't necessary, but in the
|
||||
** name of compeleteness.
|
||||
*/
|
||||
#ifdef M_TERMINFO
|
||||
resetterm();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
_O_start_standout()
|
||||
{
|
||||
if (_O_need_init)
|
||||
{
|
||||
_O_st_init();
|
||||
}
|
||||
if (_O_st_ok)
|
||||
{
|
||||
#ifdef M_TERMCAP
|
||||
(void) printf("%s",_O_startline);
|
||||
#endif
|
||||
#ifdef M_TERMINFO
|
||||
vidattr(A_STANDOUT);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
_O_doing_ul = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_O_end_standout()
|
||||
{
|
||||
if (_O_need_init)
|
||||
{
|
||||
_O_st_init();
|
||||
}
|
||||
if (_O_st_ok)
|
||||
{
|
||||
#ifdef M_TERMCAP
|
||||
(void) printf("%s",_O_endline);
|
||||
#endif
|
||||
#ifdef M_TERMINFO
|
||||
vidattr(0);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
_O_doing_ul = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_O_pchars(line,start,end)
|
||||
char *line;
|
||||
int start,end;
|
||||
{
|
||||
int cnt;
|
||||
|
||||
for(cnt=start;cnt < end; cnt++)
|
||||
{
|
||||
if (_O_doing_ul)
|
||||
{
|
||||
(void) putchar('_');
|
||||
(void) putchar('\b');
|
||||
}
|
||||
(void) putchar(line[cnt]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** convert a 0 origin token number to a 1 orgin token
|
||||
** number or 1 origin line number as appropriate
|
||||
*/
|
||||
static
|
||||
_O_con_line(numb,flags,filenum)
|
||||
int numb, flags,filenum;
|
||||
{
|
||||
if (flags & U_TOKENS)
|
||||
{
|
||||
return(numb+1);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
** check to make sure that this is a real
|
||||
** line number. if not, then return 0
|
||||
** on rare occasions, (i.e. insertion/deletion
|
||||
** of the first token in a file) we'll get
|
||||
** line numbers of -1. the usual look-up technique
|
||||
** won't work since we have no lines before than 0.
|
||||
*/
|
||||
if (numb < 0)
|
||||
return(0);
|
||||
/*
|
||||
** look up the line number the token and then
|
||||
** add 1 to make line number 1 origin
|
||||
*/
|
||||
return(L_tl2cl(filenum,numb)+1);
|
||||
}
|
||||
}
|
||||
|
||||
static char *
|
||||
_O_convert(ptr)
|
||||
char *ptr;
|
||||
{
|
||||
static char spacetext[Z_WORDLEN];
|
||||
|
||||
if (1 == strlen(ptr))
|
||||
{
|
||||
switch (*ptr)
|
||||
{
|
||||
default:
|
||||
break;
|
||||
case '\n' :
|
||||
(void) strcpy(spacetext,"<NEWLINE>");
|
||||
return(spacetext);
|
||||
case '\t' :
|
||||
(void) strcpy(spacetext,"<TAB>");
|
||||
return(spacetext);
|
||||
case ' ' :
|
||||
(void) strcpy(spacetext,"<SPACE>");
|
||||
return(spacetext);
|
||||
}
|
||||
|
||||
}
|
||||
return(ptr);
|
||||
}
|
||||
|
||||
static char*
|
||||
_O_get_text(file,index,flags)
|
||||
int file,index,flags;
|
||||
{
|
||||
static char buf[Z_LINELEN*2]; /* leave lots of room for both
|
||||
the token text and the
|
||||
chatter that preceeds it */
|
||||
char *text;
|
||||
K_token tmp;
|
||||
|
||||
if (flags & U_TOKENS)
|
||||
{
|
||||
tmp = K_gettoken(file,index);
|
||||
text = _O_convert(K_gettext(tmp));
|
||||
(void) sprintf(buf,"%s -- line %d, character %d\n",
|
||||
text,
|
||||
/*
|
||||
** add 1 to make output start at line 1
|
||||
** and character numbers start at 1
|
||||
*/
|
||||
L_tl2cl(file,K_getline(tmp))+1,
|
||||
K_getpos(tmp)+1);
|
||||
return(buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
return(L_gettline(file,index));
|
||||
}
|
||||
}
|
||||
#define _O_APP 1
|
||||
#define _O_DEL 2
|
||||
#define _O_CHA 3
|
||||
#define _O_TYPE_E 4
|
||||
|
||||
static void
|
||||
_O_do_lines(start,end,file)
|
||||
int start,end,file;
|
||||
{
|
||||
int cnt;
|
||||
int lastline = -1;
|
||||
int nextline;
|
||||
K_token nexttoken;
|
||||
for (cnt=start;cnt <= end; cnt++)
|
||||
{
|
||||
nexttoken = K_get_token(file,cnt);
|
||||
nextline = K_getline(nexttoken);
|
||||
if (lastline != nextline)
|
||||
{
|
||||
int lastone,lastchar;
|
||||
K_token lasttok;
|
||||
char linetext[Z_LINELEN+1]; /* leave room for
|
||||
terminator */
|
||||
if (0 == file)
|
||||
{
|
||||
(void) printf("< ");
|
||||
}
|
||||
else
|
||||
{
|
||||
(void) printf("> ");
|
||||
}
|
||||
|
||||
/*
|
||||
** put loop here if you want to print
|
||||
** out any intervening lines that don't
|
||||
** have any tokens on them
|
||||
*/
|
||||
|
||||
/*
|
||||
** following line is necessary because
|
||||
** L_gettline is a macro, and can't be passed
|
||||
*/
|
||||
(void) strcpy(linetext,L_gettline(file,nextline));
|
||||
_O_pchars(linetext,0,K_getpos(nexttoken));
|
||||
_O_start_standout();
|
||||
/*
|
||||
** look for last token on this line to be
|
||||
** highlighted
|
||||
*/
|
||||
for ( lastone=cnt,lasttok = K_get_token(file,lastone);
|
||||
(lastone<=end)&&(nextline == K_getline(lasttok));
|
||||
lastone++,lasttok = K_get_token(file,lastone))
|
||||
{
|
||||
}
|
||||
lastone--;
|
||||
lasttok = K_get_token(file,lastone);
|
||||
lastchar = K_getpos(lasttok)
|
||||
+ strlen(K_gettext(lasttok));
|
||||
_O_pchars(linetext,K_getpos(nexttoken),lastchar);
|
||||
_O_end_standout();
|
||||
_O_pchars(linetext,lastchar,strlen(linetext));
|
||||
|
||||
lastline = nextline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
O_output(start,flags)
|
||||
E_edit start;
|
||||
int flags;
|
||||
{
|
||||
int type = _O_TYPE_E; /* initialize to error state
|
||||
** this is to make sure that type is set
|
||||
** somewhere
|
||||
*/
|
||||
int t_beg1, t_beg2, t_end1, t_end2; /* token numbers */
|
||||
int first1, last1, first2, last2;
|
||||
|
||||
E_edit ep, behind, ahead, a, b;
|
||||
|
||||
/*
|
||||
** reverse the list of edits
|
||||
*/
|
||||
ahead = start;
|
||||
ep = E_NULL;
|
||||
while (ahead != E_NULL) {
|
||||
/*
|
||||
** set token numbers intentionally out of range
|
||||
** as boilerplate
|
||||
*/
|
||||
t_beg1 = t_beg2 = t_end1 = t_end2 = -1;
|
||||
/*
|
||||
** edit script is 1 origin, all of
|
||||
** our routines are zero origin
|
||||
*/
|
||||
E_setl1(ahead,(E_getl1(ahead))-1);
|
||||
E_setl2(ahead,(E_getl2(ahead))-1);
|
||||
|
||||
behind = ep;
|
||||
ep = ahead;
|
||||
ahead = E_getnext(ahead);
|
||||
E_setnext(ep,behind);
|
||||
}
|
||||
|
||||
/*
|
||||
** now run down the list and collect the following information
|
||||
** type of change (_O_APP, _O_DEL or _O_CHA)
|
||||
** start and length for each file
|
||||
*/
|
||||
while (ep != E_NULL)
|
||||
{
|
||||
b = ep;
|
||||
/*
|
||||
** operation always start here
|
||||
*/
|
||||
t_beg1 = E_getl1(ep);
|
||||
/*
|
||||
** any deletions will appear before any insertions,
|
||||
** so, if the first edit is an E_INSERT, then this
|
||||
** this is an _O_APP
|
||||
*/
|
||||
if (E_getop(ep) == E_INSERT)
|
||||
type = _O_APP;
|
||||
else {
|
||||
/*
|
||||
** run down the list looking for the edit
|
||||
** that is not part of the current deletion
|
||||
*/
|
||||
do {
|
||||
a = b;
|
||||
b = E_getnext(b);
|
||||
} while ((b != E_NULL) &&
|
||||
(E_getop(b) == E_DELETE) &&
|
||||
((E_getl1(b)) == ((E_getl1(a))+1)));
|
||||
/*
|
||||
** if we have an insertion at the same place
|
||||
** as the deletion we just scanned, then
|
||||
** this is a change
|
||||
*/
|
||||
if ((b != E_NULL) &&
|
||||
((E_getop(b)) == E_INSERT) &&
|
||||
((E_getl1(b))==(E_getl1(a))))
|
||||
{
|
||||
type = _O_CHA;
|
||||
}
|
||||
else
|
||||
{
|
||||
type = _O_DEL;
|
||||
}
|
||||
/*
|
||||
** set up start and length information for
|
||||
** first file
|
||||
*/
|
||||
t_end1 = E_getl1(a);
|
||||
/*
|
||||
** move pointer to beginning of insertion
|
||||
*/
|
||||
ep = b;
|
||||
/*
|
||||
** if we are showing only a deletion,
|
||||
** then we're all done, so skip ahead
|
||||
*/
|
||||
if (_O_DEL == type)
|
||||
{
|
||||
t_beg2 = E_getl2(a);
|
||||
t_end2 = -1; /* dummy number, won't
|
||||
ever be printed */
|
||||
|
||||
goto skipit;
|
||||
}
|
||||
}
|
||||
t_beg2 = E_getl2(ep);
|
||||
t_end2 = t_beg2-1;
|
||||
/*
|
||||
** now run down the list lookingfor the
|
||||
** end of this insertion and keep count
|
||||
** of the number of times we step along
|
||||
*/
|
||||
do {
|
||||
t_end2++;
|
||||
ep = E_getnext(ep);
|
||||
} while ((ep != E_NULL) && ((E_getop(ep)) == E_INSERT) &&
|
||||
((E_getl1(ep)) == (E_getl1(b))));
|
||||
|
||||
skipit:;
|
||||
if (flags & U_TOKENS)
|
||||
{
|
||||
/*
|
||||
** if we are dealing with tokens individually,
|
||||
** then just print then set printing so
|
||||
*/
|
||||
first1 = t_beg1;
|
||||
last1 = t_end1;
|
||||
first2 = t_beg2;
|
||||
last2 = t_end2;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
** we are printing differences in terms of lines
|
||||
** so find the beginning and ending lines of the
|
||||
** changes and print header in those terms
|
||||
*/
|
||||
if ( t_beg1 >= 0)
|
||||
first1 = K_getline(K_get_token(0,t_beg1));
|
||||
else
|
||||
first1 = t_beg1;
|
||||
|
||||
if ( t_end1 >= 0)
|
||||
last1 = K_getline(K_get_token(0,t_end1));
|
||||
else
|
||||
last1 = t_end1;
|
||||
|
||||
if ( t_beg2 >= 0)
|
||||
first2 = K_getline(K_get_token(1,t_beg2));
|
||||
else
|
||||
first2 = t_beg2;
|
||||
|
||||
if ( t_end2 >= 0)
|
||||
last2 = K_getline(K_get_token(1,t_end2));
|
||||
else
|
||||
last2 = t_end2;
|
||||
|
||||
}
|
||||
/*
|
||||
** print the header for this difference
|
||||
*/
|
||||
(void) printf("%d",_O_con_line(first1,flags,0));
|
||||
switch (type)
|
||||
{
|
||||
case _O_APP :
|
||||
(void) printf("a%d",_O_con_line(first2,flags,1));
|
||||
if (last2 > first2)
|
||||
{
|
||||
(void) printf(",%d",_O_con_line(last2,flags,1));
|
||||
}
|
||||
(void) printf("\n");
|
||||
break;
|
||||
case _O_DEL :
|
||||
if (last1 > first1)
|
||||
{
|
||||
(void) printf(",%d",_O_con_line(last1,flags,0));
|
||||
}
|
||||
(void) printf("d%d\n",_O_con_line(first2,flags,1));
|
||||
break;
|
||||
case _O_CHA :
|
||||
if (last1 > first1)
|
||||
{
|
||||
(void) printf(",%d",_O_con_line(last1,flags,0));
|
||||
}
|
||||
(void) printf("c%d",_O_con_line(first2,flags,1));
|
||||
if (last2 > first2)
|
||||
{
|
||||
(void) printf(",%d",_O_con_line(last2,flags,1));
|
||||
}
|
||||
(void) printf("\n");
|
||||
break;
|
||||
default:
|
||||
Z_fatal("type in O_output wasn't set\n");
|
||||
}
|
||||
if (_O_DEL == type || _O_CHA == type)
|
||||
{
|
||||
if (flags & U_TOKENS)
|
||||
{
|
||||
int cnt;
|
||||
for(cnt=first1;cnt <= last1; cnt++)
|
||||
{
|
||||
(void) printf("< %s",
|
||||
_O_get_text(0,cnt,flags));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_O_do_lines(t_beg1,t_end1,0);
|
||||
}
|
||||
}
|
||||
if (_O_CHA == type)
|
||||
{
|
||||
(void) printf("---\n");
|
||||
}
|
||||
if (_O_APP == type || _O_CHA == type)
|
||||
{
|
||||
if (flags & U_TOKENS)
|
||||
{
|
||||
int cnt;
|
||||
for(cnt=first2;cnt <= last2; cnt++)
|
||||
{
|
||||
(void) printf("> %s",
|
||||
_O_get_text(1,cnt,flags));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_O_do_lines(t_beg2,t_end2,1);
|
||||
}
|
||||
}
|
||||
}
|
||||
O_cleanup();
|
||||
return;
|
||||
}
|
17
utils/Spiff/output.h
Normal file
17
utils/Spiff/output.h
Normal file
@ -0,0 +1,17 @@
|
||||
/* Copyright (c) 1988 Bellcore
|
||||
** All Rights Reserved
|
||||
** Permission is granted to copy or use this program, EXCEPT that it
|
||||
** may not be sold for profit, the copyright notice must be reproduced
|
||||
** on copies, and credit should be given to Bellcore where it is due.
|
||||
** BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef O_INCLUDED
|
||||
|
||||
extern void O_output();
|
||||
extern void O_cleanup();
|
||||
|
||||
#define O_INCLUDED
|
||||
|
||||
#endif
|
819
utils/Spiff/paper.ms
Normal file
819
utils/Spiff/paper.ms
Normal file
@ -0,0 +1,819 @@
|
||||
.ll 6i
|
||||
.nr PO 1.15i
|
||||
.nr HM 0i
|
||||
.nr FM 1.05i
|
||||
.TL
|
||||
SPIFF -- A Program for Making Controlled Approximate Comparisons of Files
|
||||
.AU
|
||||
Daniel Nachbar
|
||||
.AI
|
||||
Software Engineering Research Group
|
||||
Bell Communications Research
|
||||
Morristown, New Jersey
|
||||
.AB
|
||||
The well known program
|
||||
.B
|
||||
diff
|
||||
.R
|
||||
[1]
|
||||
is inappropriate for some
|
||||
common tasks such as comparing the output of floating
|
||||
point calculations where roundoff errors
|
||||
lead
|
||||
.B
|
||||
diff
|
||||
.R
|
||||
astray and comparing program source code
|
||||
where some differences in the text (such as white space and comments)
|
||||
have no effect on the operation of the compiled code. A new program,
|
||||
named
|
||||
.B
|
||||
spiff,
|
||||
.R
|
||||
addresses these and other similar cases
|
||||
by lexical parsing of the input files and then applying
|
||||
a differencing algorithm to the token sequences.
|
||||
.B
|
||||
Spiff
|
||||
.R
|
||||
ignores differences
|
||||
between floating point numbers that are below a user settable tolerance.
|
||||
Other features include user settable commenting and literal string
|
||||
conventions and a choice of differencing algorithm.
|
||||
There is also an interactive mode wherein the input texts are displayed
|
||||
with differences highlighted. The user can change numeric tolerances
|
||||
"on the fly" and
|
||||
.B
|
||||
spiff
|
||||
.R
|
||||
will adjust the highlighting accordingly.
|
||||
.AE
|
||||
.SH
|
||||
Some Troubles With Diff
|
||||
.PP
|
||||
Over the past several years, it has been fairly easy to tell when
|
||||
a new type of computer arrived at a nearby computer center.
|
||||
The best clue was the discordant chorus of
|
||||
groaning, sighing, gnashing of teeth, pounding of foreheads on desks,
|
||||
and other sounds of distress. Tracing these noises to their source, one
|
||||
would find some poor soul in the process of installing
|
||||
a numerical analysis package on the new machine.
|
||||
.PP
|
||||
One might expect that "moving up" to a new machine
|
||||
would be a cause for celebration.
|
||||
After all, new machines are typically bigger, faster,
|
||||
and better than old machines.
|
||||
However, the floating point arithmetic on any new machine is frequently
|
||||
slightly different from any old machine.
|
||||
As a consequence,
|
||||
software package test routines produce output that is slightly different,
|
||||
but still correct, on the new machines.
|
||||
Serious troubles appear when the person installing the software package
|
||||
attempts to compare the test output files from two different machines
|
||||
by using a difference finding program such as
|
||||
.B
|
||||
diff.
|
||||
.R
|
||||
Programs such as
|
||||
.B
|
||||
diff
|
||||
.R
|
||||
do a character by character comparison.
|
||||
.B
|
||||
Diff
|
||||
.R
|
||||
finds a great many differences, most of which
|
||||
are due to roundoff errors in the least significant digits of floating point
|
||||
numbers. Others are the result of differences in the way
|
||||
in which the two test runs
|
||||
had printed a number (3.4e-1 vs. 0.34).
|
||||
In one case, the test suite for the S statistical analysis package[2],
|
||||
over 1700 floating point numbers are produced
|
||||
(per machine). In the eyes of
|
||||
.B
|
||||
diff,
|
||||
.R
|
||||
roughly 1200 of these numbers are different.
|
||||
However, none of the "differences" are important ones.
|
||||
Nonetheless, software installers wind up inspecting the output by eye.
|
||||
.PP
|
||||
A similar problem arises when one attempts to
|
||||
look for differences between two versions
|
||||
of the same C program.
|
||||
.B
|
||||
Diff
|
||||
.R
|
||||
reports many differences that are not of interest. In
|
||||
particular, white space (except inside quotation marks) and
|
||||
anything inside a comment have no effect on the operation of the compiled
|
||||
program and are usually not of interest.
|
||||
.B
|
||||
Diff
|
||||
.R
|
||||
does have a mode of operation where white space
|
||||
within a line (spaces and tabs) can be ignored.
|
||||
However, differences in the placement of newlines cannot be ignored.
|
||||
This is particularly annoying since C programming
|
||||
styles differ on whether to place a newline character before or after the '{'
|
||||
characters that start blocks.
|
||||
.SH
|
||||
The Problem in General Terms
|
||||
.PP
|
||||
As already mentioned, programs such as
|
||||
.B
|
||||
diff
|
||||
.R
|
||||
do
|
||||
a character-by-character comparison of the input files.
|
||||
However, when it comes to interpreting
|
||||
the contents of a file (either by a human or by a program)
|
||||
it is almost never the case that characters
|
||||
are treated individually. Rather, characters make up tokens such
|
||||
as words and numbers, or act as separators between these tokens.
|
||||
When comparing files, one is usually looking for
|
||||
differences between these tokens, not the characters that make them up
|
||||
or the characters that separate them.
|
||||
.PP
|
||||
What is needed is a program that first parses the input files
|
||||
into tokens, and then applies a differencing algorithm to the token
|
||||
sequences.
|
||||
In addition to finding differences in terms of tokens,
|
||||
it is possible to interpret the tokens and
|
||||
compare different types of tokens in different ways. Numbers, for example,
|
||||
can differ by a lot or a little.\**
|
||||
.FS
|
||||
Current differencing programs do not have such a notion because
|
||||
the difference between two characters is a binary function.
|
||||
Two characters are the same or they are not.
|
||||
.FE
|
||||
It is possible to use a tolerance when comparing two number tokens and
|
||||
report only those differences that exceed the tolerance.
|
||||
.SH
|
||||
Design Issues
|
||||
.PP
|
||||
A serious design issue for such a program is how
|
||||
complex to make the parse. The
|
||||
.I
|
||||
deeper
|
||||
.R
|
||||
one goes in the parsing the larger
|
||||
the unit of text that can be manipulated. For instance, if one is looking
|
||||
for differences in C code, a complete parse tree can be produced and
|
||||
the differencing algorithm could examine insertion and deletion of entire
|
||||
branches of the tree. However, deep parsing requires much more
|
||||
complex parsing and slower differencing algorithms.
|
||||
.PP
|
||||
Another design issue is deciding how to interpret the tokens.
|
||||
Closer interpretation may lead to greater flexibility in comparing tokens, but
|
||||
also results in a more cumbersome and error-prone implementation.
|
||||
.PP
|
||||
In the program described here, we attempt to keep both the depth
|
||||
of the parse and the semantics of the tokens to a minimum.
|
||||
The parse is a simple
|
||||
lexical parse with the input files broken up into one dimensional
|
||||
sequences of numbers, literal strings and white space.
|
||||
Literal strings and white space are not interpreted. Numbers
|
||||
are treated as representing points on the real number line.
|
||||
.SH
|
||||
Default Operation
|
||||
.PP
|
||||
.B
|
||||
Spiff\**
|
||||
.R
|
||||
.FS
|
||||
We picked the name as a way to pay a small tribute to that famous intergalactic
|
||||
adventurer Spaceman Spiff[3].
|
||||
.B
|
||||
Spiff
|
||||
.R
|
||||
is also a contraction of "spiffy diff".
|
||||
.FE
|
||||
works very much like
|
||||
.B
|
||||
diff.
|
||||
.R
|
||||
It reads two files, looks
|
||||
for differences, and prints a listing of the
|
||||
differences in the form of
|
||||
an edit script.\**
|
||||
.FS
|
||||
An edit script is a sequence of insertions and deletions
|
||||
that will transform the first file into the second.
|
||||
.FE
|
||||
As already suggested,
|
||||
.B
|
||||
spiff
|
||||
.R
|
||||
parses the files into
|
||||
literal strings and real numbers.
|
||||
The definition of these tokens can be altered somewhat by the user
|
||||
(more on this later). For now, suffice it
|
||||
to say that literals are strings like "cow", "sit",
|
||||
"into", etc. Real numbers look like "1.3", "1.6e-4" and so on.
|
||||
All of the common formats for real numbers are recognized.
|
||||
The only requirements for a string to be
|
||||
treated as a real number is the presence
|
||||
of a period and at least one digit.
|
||||
By default, a string of digits without a decimal point
|
||||
(such as "1988") is not considered to be a real number,
|
||||
but rather a literal string.\**
|
||||
Each non-alphanumeric character (such as #$@^&*)
|
||||
is parsed into a separate literal token.
|
||||
.FS
|
||||
Integer numbers are often used as indices, labels, and so on.
|
||||
Under these circumstances, it is more appropriate to treat them as literals.
|
||||
Our choice of default was driven by a design goal
|
||||
of having
|
||||
.B
|
||||
spiff
|
||||
.R
|
||||
be very conservative
|
||||
when choosing to ignore differences.
|
||||
.FE
|
||||
.PP
|
||||
Once
|
||||
.B
|
||||
spiff
|
||||
.R
|
||||
determines the two sequences of tokens,
|
||||
it compares members of the first sequence with
|
||||
members of the second sequence.
|
||||
If two tokens are of different types,
|
||||
.B
|
||||
spiff
|
||||
.R
|
||||
deems them to be different, regardless of their content.
|
||||
If both tokens are literal tokens,
|
||||
.B
|
||||
spiff
|
||||
.R
|
||||
will deem them
|
||||
to be different if any of their characters differ.
|
||||
When comparing two real numbers,
|
||||
.B
|
||||
spiff
|
||||
.R
|
||||
will deem them to be different only if
|
||||
the difference in their values exceeds a user settable tolerance.
|
||||
.SH
|
||||
Altering Spiff's Operation
|
||||
.PP
|
||||
To make
|
||||
.B
|
||||
spiff
|
||||
.R
|
||||
more generally useful, the user can control:
|
||||
.IP \(bu
|
||||
how text strings are parsed into tokens
|
||||
.IP \(bu
|
||||
how tokens of the same type are compared
|
||||
.IP \(bu
|
||||
the choice of differencing algorithm used
|
||||
.IP \(bu
|
||||
and the granularity of edit considered by the differencing algorithm.
|
||||
.LP
|
||||
.PP
|
||||
These features are described next.
|
||||
.SH
|
||||
Altering the Parse
|
||||
.PP
|
||||
The operation of the parser can be altered in several ways.
|
||||
The user can specify that delimited sections of text are to be ignored
|
||||
completely. This is useful for selectively ignoring the contents of
|
||||
comments in programs. Similarly, the user can specify that
|
||||
delimited sections of text (including white space)
|
||||
be treated as a single literal token. So, literal strings in program
|
||||
text can be treated appropriately.
|
||||
Multiple sets of
|
||||
delimiters may be specified at once (to handle cases such as the
|
||||
Modula-2 programming language
|
||||
where there are two ways to specify quoted strings). At present,
|
||||
the delimiters must be fixed string (possibly restricted to the
|
||||
beginning of the line) or end of line.
|
||||
As a consequence of the mechanism for specifying literal strings,
|
||||
multicharacter operators (such as the += operator in C)
|
||||
can be parsed into a single token.
|
||||
.PP
|
||||
As yet, no provision is made for allowing delimiter
|
||||
specification in terms of regular expressions. This omission
|
||||
was made for the sake of simplifying the parser.
|
||||
Nothing prevents the addition of regular expressions in the
|
||||
future. However, the simple mechanism
|
||||
already in place handles the literal string and commenting conventions
|
||||
for most well known programming languages.\**
|
||||
.FS
|
||||
See the manual page in the appendix for examples of handling
|
||||
C, Bourne Shell, Fortran, Lisp, Pascal, and Modula-2. The only
|
||||
cases that are known not to work are comments in BASIC and
|
||||
Hollerith strings in Fortran.
|
||||
.FE
|
||||
.PP
|
||||
In addition to controlling literal string and comments, the user
|
||||
may also specify whether to treat white space characters as any other
|
||||
non-alphanumeric character (in other words, parse each white space
|
||||
character into its own literal token),
|
||||
whether to parse sign markers as part
|
||||
of the number that they precede or as separate tokens, whether
|
||||
to treat numbers without printed decimal markers (e.g. "1988")
|
||||
as real numbers rather than as literal strings, and whether
|
||||
to parse real numbers into literal tokens.
|
||||
.SH
|
||||
Altering the Comparison of Individual Tokens
|
||||
.PP
|
||||
As mentioned earlier, the user can set a tolerance below which differences
|
||||
between real numbers are ignored.
|
||||
.B
|
||||
Spiff
|
||||
.R
|
||||
allows two kinds of tolerances:
|
||||
absolute and relative.
|
||||
Specifying an absolute tolerance will cause
|
||||
.B
|
||||
spiff
|
||||
.R
|
||||
to ignore differences
|
||||
that are less than the specified value.
|
||||
For instance, specifying an absolute tolerance of 0.01 will
|
||||
cause only those differences greater than or equal to 0.01 to be reported.
|
||||
Specifying a relative tolerance will cause
|
||||
.B
|
||||
spiff
|
||||
.R
|
||||
to ignore differences that are
|
||||
smaller than some fraction of the number of larger magnitude.
|
||||
Specifically, the value of the tolerance is interpreted
|
||||
as a fraction of the larger (in absolute terms)
|
||||
of the two floating point numbers being compared.
|
||||
For example,
|
||||
specifying a relative tolerance of 0.1
|
||||
will cause the two floating point numbers 1.0 and 0.91 to be deemed within
|
||||
tolerance. The numbers 1.0 and 0.9 will be outside the tolerance.
|
||||
Absolute and relative tolerances can be OR'ed together. In fact,
|
||||
the most effective way to ignore differences that are due to roundoff errors
|
||||
in floating point calculations is to use both
|
||||
a relative tolerance (to handle limits in precision) as well as an absolute
|
||||
tolerance (to handle cases when one number is zero and the other number is
|
||||
almost zero).\**
|
||||
.FS
|
||||
All numbers differ from zero by 100% of their magnitude. Thus, to handle
|
||||
numbers that are near zero, one would have to specify a relative tolerance
|
||||
of 100% which would be unreasonably large when both numbers are non-zero.
|
||||
.FE
|
||||
In addition, the user can specify an infinite tolerance. This is useful
|
||||
for checking the format of output while ignoring the actual numbers
|
||||
produced.
|
||||
.SH
|
||||
Altering the Differencing Algorithm
|
||||
.PP
|
||||
By default,
|
||||
.B
|
||||
spiff
|
||||
.R
|
||||
produces a minimal edit sequence (using the Miller/Myers differencing algorithm[4])
|
||||
that will convert the first file into the second.
|
||||
However, a minimal edit sequences is not always desirable.
|
||||
For example, for the following two tables of numbers:
|
||||
.DS
|
||||
0.1 0.2 0.3 0.2 0.3 0.4
|
||||
0.4 0.5 0.6 0.5 0.6 0.7
|
||||
.DE
|
||||
a minimal edit sequence to convert the table on
|
||||
the left into the table on the right be to
|
||||
would delete the first number (0.1) and insert 0.7 at the end.\**
|
||||
.FS
|
||||
The problem of having the elements of tables become misaligned when
|
||||
the differencing algorithm is trying
|
||||
to find a minimal number of edits can be reduced somewhat
|
||||
by retaining newlines and not using tolerances.
|
||||
Unfortunately, it does not go away.
|
||||
.FE
|
||||
Such a result, while logically correct, does not provide a good picture
|
||||
of the differences between the two files.
|
||||
In general, for text with a very definite structure (such as tables),
|
||||
we may not want to consider insertions and deletions at all, but
|
||||
only one-to-one changes.\**
|
||||
.FS
|
||||
A "change" can be expressed as one deletion and one insertion at the same
|
||||
point in the text.
|
||||
.FE
|
||||
So, rather than look for a minimal edit script, we
|
||||
merely want to compare each token in the first file with
|
||||
the corresponding token in the second file.
|
||||
.PP
|
||||
The user can choose which differencing algorithm to use
|
||||
(the default Miller/Myers or
|
||||
the alternative one-to-one comparison)
|
||||
based upon what is known about the input files. In general,
|
||||
files produced mechanically
|
||||
(such the output from test suites) have a very regular structure
|
||||
and the one-to-one comparison works surprisingly well.
|
||||
For files created by humans, the Miller/Myers
|
||||
algorithm is more appropriate.
|
||||
There is nothing in
|
||||
.B
|
||||
spiff's
|
||||
.R
|
||||
internal design that limits
|
||||
the number of differencing algorithms that it can run.
|
||||
Other differencing algorithms,
|
||||
in particular the one used in
|
||||
.B
|
||||
diff,
|
||||
.R
|
||||
will probably be added later.
|
||||
.SH
|
||||
Altering the Granularity of the Edit Sequence
|
||||
.PP
|
||||
By default,
|
||||
.B
|
||||
spiff
|
||||
.R
|
||||
produces an edit sequence
|
||||
in terms of insertions and deletions of individual tokens.
|
||||
At times it may be more useful to
|
||||
treat the contents of the files as tokens when looking for differences
|
||||
but
|
||||
express the edit script in terms of entire lines of the files rather
|
||||
than individual tokens.\**
|
||||
.FS
|
||||
For instance, if one wants to have
|
||||
.B
|
||||
spiff
|
||||
.R
|
||||
produce output that can be fed into
|
||||
the
|
||||
.B
|
||||
ed
|
||||
.R
|
||||
editor.
|
||||
.FE
|
||||
.B
|
||||
Spiff
|
||||
.R
|
||||
provides a facility for restricting the edits to entire lines.
|
||||
.SH
|
||||
Treating Parts of the Files Differently
|
||||
.PP
|
||||
For complex input files, it is important that different parts of the
|
||||
file be treated in different ways. In other words, it may be impossible
|
||||
to find one set of parsing/differencing rules that work well for the
|
||||
entire file.
|
||||
.B
|
||||
Spiff
|
||||
.R
|
||||
can differentiate between parts of the input files on two bases:
|
||||
within a line and between lines.
|
||||
Within a line, a different tolerance can be applied to each real number.
|
||||
The tolerances are specified in terms of the ordinal position of the
|
||||
numbers on the line (i.e. one tolerance is applied to the first real number
|
||||
on each line, a different tolerance is applied to the second number on
|
||||
each line, a third tolerance is applied to the third, and so on). If more
|
||||
numbers appear on a line than there are tolerances specified, the last
|
||||
tolerance is applied to all subsequent numbers on the line (i.e., if the user
|
||||
specifies three tolerances, the third is applied to the third, fourth
|
||||
fifth, . . . number on each line). This feature is useful for applying
|
||||
different tolerances to the different columns of a table of numbers.
|
||||
.PP
|
||||
Between lines, the user can place "embedded commands" in the input files.
|
||||
These commands
|
||||
are instructions to parser that can change what tolerances are attached
|
||||
to real numbers and the commenting and literal string conventions used by the
|
||||
parser. Embedded commands are flagged to the parser
|
||||
by starting the line with a user-specified
|
||||
escape string. By combining within line and between line differentiation,
|
||||
it is possible for the user to specify a different tolerance
|
||||
for every single real number in the input files.
|
||||
.SH
|
||||
Visual Mode
|
||||
.PP
|
||||
So far,
|
||||
.B
|
||||
spiff's
|
||||
.R
|
||||
operation as an intelligent filter has been described.
|
||||
.B
|
||||
Spiff
|
||||
.R
|
||||
also has an interactive mode.
|
||||
When operating in interactive mode,
|
||||
.B
|
||||
spiff
|
||||
.R
|
||||
places corresponding sections of the input files
|
||||
side by side on user's screen.\**
|
||||
.FS
|
||||
Although the current implementation of
|
||||
.B
|
||||
spiff
|
||||
.R
|
||||
runs in many environments,
|
||||
interactive mode works only under the MGR window manager.[5]
|
||||
Other graphics interfaces will probably be added over time.
|
||||
.FE
|
||||
Tokens are compared using a one-to-one ordinal comparison, and any tokens that
|
||||
are found to be different are highlighted in reverse video.
|
||||
The user can interactively change the tolerances and
|
||||
.B
|
||||
spiff
|
||||
.R
|
||||
will alter the display
|
||||
to reflect which real numbers exceed the new tolerances.
|
||||
Other commands allow the user to page through the file and exit.
|
||||
.SH
|
||||
Performance
|
||||
.PP
|
||||
Two components of
|
||||
.B
|
||||
spiff,
|
||||
.R
|
||||
the parser and the differencing algorithm,
|
||||
account for most of the execution time. Miller and Myers compare their
|
||||
algorithm to the one used in the diff program. To restate their results,
|
||||
the Miller/Myers algorithm is faster for files
|
||||
that have relatively few differences but much
|
||||
slower (quadratic time) for files with a great many differences.
|
||||
.PP
|
||||
For cases where the files do not differ greatly,
|
||||
parsing the input files takes most of the time (around 80% of the total).\**
|
||||
.FS
|
||||
No effort has yet been made to make the parser run more quickly.
|
||||
A faster parser could no doubt be written by generating a special state machine.
|
||||
.FE
|
||||
The performance of the parser is roughly similar to programs that do a similar
|
||||
level of parsing (i.e. programs that must examine each character in the file).
|
||||
For files where roughly half of the tokens are real numbers,
|
||||
.B
|
||||
spiff
|
||||
.R
|
||||
takes about twice as long to parse the input files
|
||||
as an
|
||||
.B
|
||||
awk
|
||||
.R
|
||||
program that counts the number of words in a file:\**
|
||||
.FS
|
||||
For
|
||||
.B
|
||||
awk,
|
||||
.R
|
||||
a word is any string separated by white space.
|
||||
.FE
|
||||
.B
|
||||
.DS
|
||||
awk '{total += NF}' firstfile secondfile
|
||||
.DE
|
||||
.R
|
||||
.PP
|
||||
The time that it takes
|
||||
.B
|
||||
spiff
|
||||
.R
|
||||
to parse a file is substantially
|
||||
increased if scanning is done for comments
|
||||
and delimited literal strings. The precise effect depends upon the length of
|
||||
the delimiters, whether they are restricted to appear at beginning of line, and
|
||||
the frequency with which literals and comments appear in the input files.
|
||||
As an example, adding the 12 literal conventions\**
|
||||
.FS
|
||||
One literal convention is for C literal strings. The rest enumerate multicharacter
|
||||
operators.
|
||||
.FE
|
||||
and 1 commenting convention
|
||||
required for C code roughly doubles the time required to parse input files.\**
|
||||
.FS
|
||||
So in total, it takes
|
||||
.B
|
||||
spiff
|
||||
.R
|
||||
about 4 times longer to parse a C program than it takes
|
||||
.B
|
||||
awk
|
||||
.R
|
||||
to count the number of words in the same file.
|
||||
.FE
|
||||
.PP
|
||||
A more complete approach to evaluating
|
||||
.B
|
||||
spiff's
|
||||
.R
|
||||
performance must measure the total time that it takes for the user to complete a
|
||||
differencing task. For example, consider one of the
|
||||
test suites for the S statistical
|
||||
analysis package mentioned at the beginning of this paper.
|
||||
The output file for each machine is 427 lines long and contains
|
||||
1090 floating point numbers. It takes
|
||||
.B
|
||||
diff
|
||||
.R
|
||||
approximately 2 seconds on one of our "6 MIPS"\** computers
|
||||
.FS
|
||||
We will not comment on the usefulness of "MIPS" as a measure
|
||||
of computing speed. The numbers provided are only intended to
|
||||
give the reader some vague idea of how fast these programs run.
|
||||
.FE
|
||||
to compare the two files and produce
|
||||
an edit script that is 548 lines long containing 1003 "differences"
|
||||
in the floating point numbers. It takes the average tester
|
||||
5 minutes to print out the edit script and roughly 2 hours to examine
|
||||
the output by hand to determine that the machines are, in fact,
|
||||
both giving nearly identical answers. The total time needed is
|
||||
2 hours 5 minutes and 2 seconds.
|
||||
.PP
|
||||
In contrast, it takes
|
||||
.B
|
||||
spiff
|
||||
.R
|
||||
approximately 6 seconds on one of our "6 MIPS" computers to
|
||||
produce an output file that is 4 lines long.\**
|
||||
.FS
|
||||
The output would be zero length except that the output of the
|
||||
.B
|
||||
time
|
||||
.R
|
||||
command is built into the S tests.
|
||||
The timing information could easily be ignored using
|
||||
.B
|
||||
spiff's
|
||||
.R
|
||||
embedded commands. But, as we shall see, it hardly seems worth the trouble.
|
||||
.FE
|
||||
It takes the average tester 30 seconds to examine
|
||||
.B
|
||||
spiff's
|
||||
.R
|
||||
output. The total for
|
||||
.B
|
||||
spiff
|
||||
.R
|
||||
is 36 seconds. Therefore for this case,
|
||||
.B
|
||||
spiff
|
||||
.R
|
||||
will get the job done roughly 208.88 times faster than
|
||||
.B
|
||||
diff.
|
||||
.R
|
||||
.PP
|
||||
In general, it is misleading to compare
|
||||
.B
|
||||
spiff's
|
||||
.R
|
||||
speed with that of
|
||||
.B
|
||||
diff.
|
||||
.R
|
||||
While both programs are looking for differences between files,
|
||||
they operate on very different types of data (tokens vs. bytes).
|
||||
An analogous comparison could be made between the speed of an assembler
|
||||
and the speed of a C compiler. They are both language translators.
|
||||
One runs much faster than the other.
|
||||
None the less, most programmers use the slower program
|
||||
whenever possible.
|
||||
.SH
|
||||
Using Spiff For Making Regression Tests Of Software
|
||||
.PP
|
||||
We envision
|
||||
.B
|
||||
spiff
|
||||
.R
|
||||
to be the first of several tools for aiding in the now
|
||||
arduous task of making regression tests.\**
|
||||
.FS
|
||||
In software engineering parlance, a "regression test" is the process by
|
||||
which a tester checks to make sure that the new version of a piece of
|
||||
software still performs the same way as the older versions
|
||||
on overlapping tasks.
|
||||
.FE
|
||||
Given
|
||||
.B
|
||||
spiff's
|
||||
.R
|
||||
current capabilities, the regression test designer can
|
||||
take the output of an older version of software and through
|
||||
the use of literal string and commenting conventions,
|
||||
specify what parts of the output must remain identical and
|
||||
what sections can change completely. By specifying tolerances, the test
|
||||
designer can take into account how much of a difference in floating
|
||||
point calculations is acceptable.
|
||||
.PP
|
||||
The test designer is also free to
|
||||
edit the output from the older version of the software and add embedded
|
||||
commands that can instruct
|
||||
.B
|
||||
spiff
|
||||
.R
|
||||
to treat various parts of the output
|
||||
differently. The newly edited output can then serve as a template for
|
||||
the output of later versions of the software.
|
||||
.PP
|
||||
Obviously, editing output by hand is a very low level mechanism for adding
|
||||
specification information. It is our intention that
|
||||
.B
|
||||
spiff
|
||||
.R
|
||||
will become
|
||||
the last element in a pipeline of programs. Programs (as yet unwritten) located
|
||||
earlier in the pipeline
|
||||
can implement a higher level representation of the specification information.
|
||||
They read in the old and new input files, add the appropriate embedded commands,
|
||||
and then pass the results to
|
||||
.B
|
||||
spiff
|
||||
.R
|
||||
which will do the actual differencing.
|
||||
.SH
|
||||
Future Work
|
||||
.PP
|
||||
There are many features that could be added to
|
||||
.B
|
||||
spiff
|
||||
.R
|
||||
(if there are not
|
||||
too many already). Some of these include:
|
||||
.IP \(bu
|
||||
Using separate differencing algorithms on separate sections of the file
|
||||
and/or limiting the scope of an edit sequence (fencing)
|
||||
.IP \(bu
|
||||
Providing a more general mechanism for specifying comments and literals
|
||||
(perhaps allowing specification in terms of regular expressions).
|
||||
As yet, we have not encountered any important cases where regular expressions
|
||||
have been needed. Until such a case is encountered, we will leave regular
|
||||
expressions out in the name of simplicity.
|
||||
.IP \(bu
|
||||
Allowing for a more general specification of what lines should look like.
|
||||
At present, the user can only specify tolerances for numbers as a function
|
||||
of their ordinal position on a line. The difficulty in expanding the
|
||||
specification abilities of
|
||||
.B
|
||||
spiff
|
||||
.R
|
||||
is knowing when to stop. In the extreme,
|
||||
we might add all of the functionality of a program such as
|
||||
.B
|
||||
awk.\**
|
||||
.R
|
||||
.FS
|
||||
Imagine handling the case such as
|
||||
"apply this tolerance to all numbers that appear
|
||||
on a line starting with the word `foo' but only if the number is between 1.9
|
||||
and 3.6 and the word `bar' does not appear on the line".
|
||||
.FE
|
||||
We hope to keep
|
||||
.B
|
||||
spiff
|
||||
.R
|
||||
as simple as possible. Our first efforts in
|
||||
this direction will try to implement higher level specification functions
|
||||
outside of
|
||||
.B
|
||||
spiff.
|
||||
.R
|
||||
.SH
|
||||
Acknowledgements
|
||||
.PP
|
||||
First and foremost, we thank Stu Feldman for his endless patience, constant encouragement
|
||||
and numerous good ideas. We also extend thanks to Doug McIlroy for bringing the Miller/Myers
|
||||
algorithm to our attention, Nat Howard for a key insight
|
||||
and for his editorial comments
|
||||
and Steve Uhler and Mike Bianchi for their editorial comments.
|
||||
.SH
|
||||
References
|
||||
.IP [1]
|
||||
Hunt,J.W. and M.D. McIlroy.
|
||||
.I
|
||||
An Algorithm For Differential File Comparisons,
|
||||
.R
|
||||
.B
|
||||
Bell Labs Computer Science Technical Report,
|
||||
.R
|
||||
Number 41, 1975.
|
||||
.IP [2]
|
||||
Becker,R.A. and J.M. Chambers (1984).
|
||||
.B
|
||||
S \- An Interactive Environment For Data Analysis And
|
||||
Graphics.
|
||||
.R
|
||||
Belmont, CA: Wadsworth Inc.
|
||||
.IP [3]
|
||||
Watterson, B. (1987).
|
||||
.B
|
||||
Calvin and Hobbes.
|
||||
.R
|
||||
New York: Andrews, McMeel & Parker.
|
||||
.IP [4]
|
||||
Miller, W. and E.W. Myers.
|
||||
.I
|
||||
A File Comparison Program,
|
||||
.R
|
||||
.B
|
||||
Software \-
|
||||
Practice and Experience
|
||||
.R
|
||||
15, 11, 1025-1040, 1985.
|
||||
.IP [5]
|
||||
Uhler, S.A.
|
||||
.I
|
||||
MGR -- A Window Manager For UNIX,
|
||||
.R
|
||||
Sun User's Group Meeting. September 1986.
|
||||
.LP
|
802
utils/Spiff/parse.c
Normal file
802
utils/Spiff/parse.c
Normal file
@ -0,0 +1,802 @@
|
||||
/* Copyright (c) 1988 Bellcore
|
||||
** All Rights Reserved
|
||||
** Permission is granted to copy or use this program, EXCEPT that it
|
||||
** may not be sold for profit, the copyright notice must be reproduced
|
||||
** on copies, and credit should be given to Bellcore where it is due.
|
||||
** BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef lint
|
||||
static char rcsid[]= "$Header$";
|
||||
#endif
|
||||
|
||||
#include "misc.h"
|
||||
#include "flagdefs.h"
|
||||
#include "float.h"
|
||||
#include "tol.h"
|
||||
#include "token.h"
|
||||
#include "line.h"
|
||||
#include "command.h"
|
||||
#include "comment.h"
|
||||
#include "parse.h"
|
||||
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#define _P_PARSE_CHATTER 1000
|
||||
|
||||
|
||||
static int _P_realline; /* loop counter */
|
||||
static int _P_fnumb;
|
||||
|
||||
static char *_P_nextchr; /* pointer to the next character to parse */
|
||||
static char *_P_firstchr; /* pointer to the beginning of the line being parsed */
|
||||
static int _P_next_tol; /* number of floats seen on this line */
|
||||
static int _P_stringsize; /* count of number of characters that are being
|
||||
read into a comment or literal */
|
||||
static int _P_has_content; /* flag to indicate if the line being
|
||||
parsed has any tokens on it */
|
||||
static int _P_start; /* first line to parse */
|
||||
static int _P_lcount; /* number of lines to parse */
|
||||
|
||||
static int _P_flags; /* location for global flags */
|
||||
|
||||
/*
|
||||
** by default, "words" can be made up of numbers and letters
|
||||
** the following code allows for extending the alphabet that can
|
||||
** be used in words. this is useful for handling languages such
|
||||
** as C where the underscore character is an allowable character
|
||||
** in an identifier. If a character (such as underscore) is NOT added
|
||||
** to the alphabet, the identifier will be broken into 2 or more "words"
|
||||
** by the parser. as such the two sequences
|
||||
** one_two
|
||||
** and
|
||||
** one _ two
|
||||
** would look identical to spiff.
|
||||
*/
|
||||
#define _P_ALPHALEN 256
|
||||
static char _P_alpha[_P_ALPHALEN];
|
||||
|
||||
static void
|
||||
_P_alpha_clear()
|
||||
{
|
||||
*_P_alpha = '\0';
|
||||
}
|
||||
|
||||
static
|
||||
_P_in_alpha(chr)
|
||||
char chr;
|
||||
{
|
||||
#ifndef ATT
|
||||
extern int index();
|
||||
#endif
|
||||
/*
|
||||
** special case when string terminator
|
||||
** is handed to us
|
||||
*/
|
||||
if ('\0' == chr)
|
||||
return(0);
|
||||
|
||||
#ifdef ATT
|
||||
return((int) strchr(_P_alpha,chr));
|
||||
#else
|
||||
return((int) index(_P_alpha,chr));
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
P_addalpha(ptr)
|
||||
char *ptr;
|
||||
{
|
||||
char buf[Z_LINELEN];
|
||||
|
||||
S_wordcpy(buf,ptr); /* copy up to (but not including)
|
||||
the first whitespace char */
|
||||
|
||||
if ((strlen(_P_alpha) + strlen(buf)) >= _P_ALPHALEN)
|
||||
{
|
||||
Z_fatal("too many characters added to extended alphabet");
|
||||
}
|
||||
(void) strcat(_P_alpha,buf);
|
||||
}
|
||||
|
||||
/*
|
||||
** put parser in a default state
|
||||
*/
|
||||
|
||||
static char _P_dummyline[2]; /* a place to aim wild pointers */
|
||||
static void
|
||||
_P_initparser()
|
||||
{
|
||||
_P_dummyline[0] = '\0';
|
||||
|
||||
/*
|
||||
** now reset all the state of each module
|
||||
*/
|
||||
C_clear_cmd(); /* disable embedded command key word */
|
||||
T_clear_tols();
|
||||
W_clearcoms();
|
||||
W_clearlits();
|
||||
_P_alpha_clear(); /* disable extended alphabet */
|
||||
|
||||
/*
|
||||
** and set state as defined by execute-time commands.
|
||||
*/
|
||||
C_docmds();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
_P_needmore()
|
||||
{
|
||||
return(*_P_nextchr == '\0');
|
||||
}
|
||||
|
||||
static
|
||||
_P_nextline()
|
||||
{
|
||||
/*
|
||||
** if the line that we just finished had
|
||||
** some content, increment the count
|
||||
*/
|
||||
if (_P_has_content)
|
||||
{
|
||||
L_incclmax(_P_fnumb);
|
||||
/*
|
||||
** if the previous line had a token
|
||||
** increment the line
|
||||
*/
|
||||
if (L_getcount(_P_fnumb,L_gettlmax(_P_fnumb)))
|
||||
{
|
||||
L_inctlmax(_P_fnumb);
|
||||
L_setcount(_P_fnumb,L_gettlmax(_P_fnumb),0);
|
||||
}
|
||||
_P_has_content = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** reset the number of floats seen on the line
|
||||
*/
|
||||
_P_next_tol = 0;
|
||||
|
||||
/*
|
||||
** get another line if there is one available
|
||||
*/
|
||||
_P_realline++;
|
||||
if (_P_realline >= _P_start+_P_lcount)
|
||||
{
|
||||
return(1);
|
||||
}
|
||||
|
||||
_P_firstchr = _P_nextchr = L_getrline(_P_fnumb,_P_realline);
|
||||
/*
|
||||
** and look for a command
|
||||
*/
|
||||
if (C_is_cmd(_P_firstchr))
|
||||
{
|
||||
_P_nextchr = _P_dummyline;
|
||||
_P_has_content = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
** we have a real line, so set up the index
|
||||
*/
|
||||
L_setclindex(_P_fnumb,L_getclmax(_P_fnumb),_P_realline);
|
||||
_P_has_content = 1;
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
** the following three routines (_P_litsnarf, _P_bolsnarf, and _P_comsnarf
|
||||
** all do roughly the same thing. they scan ahead and collect the
|
||||
** specified string, move _P_nextchr to the end of the
|
||||
** comment or literal and return 1 if we run off the end of file,
|
||||
** 0 otherwise. it would have been nice to have 1 routine handle
|
||||
** all three task (there is much common code), however there were
|
||||
** so enough differences, (for instance, only comments check for nesting,
|
||||
** only literals need to set _P_stringsize, etc)
|
||||
** that I decided to split them up.
|
||||
*/
|
||||
static int
|
||||
_P_litsnarf(litptr)
|
||||
W_lit litptr;
|
||||
{
|
||||
_P_stringsize = 0;
|
||||
/*
|
||||
** skip the start of literal string
|
||||
*/
|
||||
_P_nextchr += strlen(W_litbegin(litptr));
|
||||
_P_stringsize += strlen(W_litbegin(litptr));
|
||||
/*
|
||||
** is there a separate end string?
|
||||
** if not, then we're done
|
||||
*/
|
||||
if ('\0' == *(W_litend(litptr)))
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
/*
|
||||
** loop once for each character in the literal
|
||||
*/
|
||||
while(1)
|
||||
{
|
||||
/*
|
||||
** if we are out of characters, move on to next line
|
||||
*/
|
||||
if (_P_needmore())
|
||||
{
|
||||
if (_P_nextline())
|
||||
{
|
||||
return(1);
|
||||
}
|
||||
if (!_P_has_content)
|
||||
{
|
||||
/*
|
||||
** since we've just gotten a command
|
||||
** check to see if this literal
|
||||
** is still legit ...
|
||||
** could have just been reset
|
||||
** by the command
|
||||
*/
|
||||
if (!W_is_lit(litptr))
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
} /* if _P_needmore */
|
||||
|
||||
/*
|
||||
** see if we have an escaped end of literal string
|
||||
*/
|
||||
if (('\0' != *(W_litescape(litptr))) && /* escape string exists */
|
||||
!S_wordcmp(_P_nextchr,
|
||||
W_litescape(litptr)) && /* and escape matches */
|
||||
!S_wordcmp(_P_nextchr+strlen(W_litescape(litptr)),
|
||||
W_litend(litptr))) /* and endstring matches */
|
||||
{
|
||||
_P_nextchr += strlen(W_litescape(litptr))
|
||||
+ strlen(W_litend(litptr));
|
||||
_P_stringsize += strlen(W_litescape(litptr))
|
||||
+ strlen(W_litend(litptr));
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
** see if we have an end of literal string
|
||||
*/
|
||||
if (!S_wordcmp(_P_nextchr,W_litend(litptr))) /* escape matches */
|
||||
{
|
||||
_P_nextchr += strlen(W_litend(litptr));
|
||||
_P_stringsize += strlen(W_litend(litptr));
|
||||
return(0);
|
||||
}
|
||||
/*
|
||||
** this must be yet another character in the literal, so
|
||||
** just snarf it up
|
||||
*/
|
||||
_P_nextchr++;
|
||||
_P_stringsize++;
|
||||
} /* while loop once for each character */
|
||||
|
||||
#ifndef lint
|
||||
Z_fatal("shouldn't execute this line at the end of _P_litsnarf");
|
||||
#endif
|
||||
} /* _P_litsnarf */
|
||||
|
||||
static int
|
||||
_P_bolsnarf(bolptr)
|
||||
W_bol bolptr;
|
||||
{
|
||||
/*
|
||||
** skip the start of comment string
|
||||
*/
|
||||
_P_nextchr += strlen(W_bolbegin(bolptr));
|
||||
/*
|
||||
** is there a separate end string
|
||||
** if not, then we're done
|
||||
*/
|
||||
if ('\0' == *(W_bolend(bolptr)))
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
/*
|
||||
** loop once for each character in the comment
|
||||
*/
|
||||
while(1)
|
||||
{
|
||||
/*
|
||||
** if we are out of characters,move on to next line
|
||||
*/
|
||||
if (_P_needmore())
|
||||
{
|
||||
if (_P_nextline())
|
||||
{
|
||||
return(1);
|
||||
}
|
||||
if (!_P_has_content)
|
||||
{
|
||||
/*
|
||||
** since we've just gotten a command
|
||||
** check to see if this comment
|
||||
** is still legit ... comments
|
||||
** could have just been reset
|
||||
** by the command
|
||||
*/
|
||||
if (!W_is_bol(bolptr))
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
} /* if at end of line */
|
||||
|
||||
/*
|
||||
** see if we have an escaped end of comment string
|
||||
*/
|
||||
if ('\0' != *(W_bolescape(bolptr)) && /* escape string exists */
|
||||
!S_wordcmp(_P_nextchr,
|
||||
W_bolescape(bolptr)) && /* and escape matches */
|
||||
!S_wordcmp(_P_nextchr+strlen(W_bolescape(bolptr)),
|
||||
W_bolend(bolptr))) /* and end string matches */
|
||||
{
|
||||
_P_nextchr += strlen(W_bolescape(bolptr))
|
||||
+ strlen(W_bolend(bolptr));
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
** see if we have an end of comment string
|
||||
*/
|
||||
if (!S_wordcmp(_P_nextchr,W_bolend(bolptr)))
|
||||
{
|
||||
_P_nextchr += strlen(W_bolend(bolptr));
|
||||
return(0);
|
||||
}
|
||||
/*
|
||||
** this must be yet another character in the comment, so
|
||||
** just snarf it up
|
||||
*/
|
||||
_P_nextchr++;
|
||||
} /* while loop once for each character */
|
||||
|
||||
#ifndef lint
|
||||
Z_fatal("shouldn't execute this line in at end of _P_bolsnarf");
|
||||
#endif
|
||||
} /* _P_bolsnarf */
|
||||
|
||||
/*
|
||||
** pass over a comment -- look for nexting
|
||||
*/
|
||||
static
|
||||
_P_comsnarf(comptr)
|
||||
W_com comptr;
|
||||
{
|
||||
int depth = 1; /* nesting depth */
|
||||
/*
|
||||
** skip the start of comment string
|
||||
*/
|
||||
_P_nextchr += strlen(W_combegin(comptr));
|
||||
|
||||
/*
|
||||
** is there a separate end string
|
||||
** if not, then we're done
|
||||
*/
|
||||
if ('\0' == *(W_comend(comptr)))
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
/*
|
||||
** loop once for each character in the comment
|
||||
*/
|
||||
while(1)
|
||||
{
|
||||
/*
|
||||
** if we are out of characters, move on to next line
|
||||
*/
|
||||
if (_P_needmore())
|
||||
{
|
||||
if (_P_nextline())
|
||||
{
|
||||
return(1);
|
||||
}
|
||||
if (!_P_has_content)
|
||||
{
|
||||
/*
|
||||
** since we've just gotten a command
|
||||
** check to see if this comment
|
||||
** is still legit ... comments
|
||||
** could have just been reset
|
||||
** by the command
|
||||
*/
|
||||
if (!W_is_com(comptr))
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
} /* if at end of line */
|
||||
|
||||
/*
|
||||
** see if we have an escaped end of comment string
|
||||
*/
|
||||
if ('\0' != *(W_comescape(comptr)) && /* escape string exists */
|
||||
!S_wordcmp(_P_nextchr,
|
||||
W_comescape(comptr)) && /* and escape matches */
|
||||
!S_wordcmp(_P_nextchr+strlen(W_comescape(comptr)),
|
||||
W_comend(comptr))) /* and end string matches */
|
||||
{
|
||||
/*
|
||||
** skip over the escape sequence and the end sequence
|
||||
*/
|
||||
_P_nextchr += strlen(W_comescape(comptr))
|
||||
+ strlen(W_comend(comptr));
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
** see if we have an end of comment string
|
||||
*/
|
||||
if (!S_wordcmp(_P_nextchr,W_comend(comptr))) /* end matches */
|
||||
{
|
||||
/*
|
||||
** skip over the end sequence
|
||||
*/
|
||||
_P_nextchr += strlen(W_comend(comptr));
|
||||
if (W_is_nesting(comptr))
|
||||
{
|
||||
depth--;
|
||||
if (0 == depth)
|
||||
return(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
** see if we have another beginning of comment string
|
||||
*/
|
||||
if (W_is_nesting(comptr) &&
|
||||
!S_wordcmp(_P_nextchr,W_comend(comptr))) /* end matches */
|
||||
{
|
||||
_P_nextchr += strlen(W_comend(comptr));
|
||||
depth++;
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
** this must be yet another character in the comment, so
|
||||
** just snarf it up
|
||||
*/
|
||||
_P_nextchr++;
|
||||
} /* while loop once for each character */
|
||||
|
||||
#ifndef lint
|
||||
Z_fatal("should not execute this line in _P_comsnarf\n");
|
||||
#endif
|
||||
|
||||
} /* _P_comsnarf */
|
||||
|
||||
|
||||
/*
|
||||
** parse a file
|
||||
*/
|
||||
static void
|
||||
_P_do_parse()
|
||||
{
|
||||
|
||||
char *ptr; /* scratch space */
|
||||
int tmp;
|
||||
int ret_code;
|
||||
|
||||
K_token newtoken;
|
||||
W_bol bolptr;
|
||||
W_com comptr;
|
||||
W_lit litptr;
|
||||
|
||||
int startline, endline, startpos;
|
||||
|
||||
/*
|
||||
** main parsing loop
|
||||
*/
|
||||
while (1)
|
||||
{
|
||||
/*
|
||||
** get more text if necessary
|
||||
*/
|
||||
if (_P_needmore())
|
||||
{
|
||||
if (_P_nextline())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
** if the line contains nothing of interest,
|
||||
** try again
|
||||
*/
|
||||
if (!_P_has_content)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
** check to see if this line starts a comment
|
||||
*/
|
||||
if ((bolptr = W_isbol(_P_firstchr)) != W_BOLNULL)
|
||||
{
|
||||
if (_P_bolsnarf(bolptr))
|
||||
{
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
} /* if _P_needmore */
|
||||
|
||||
/*
|
||||
** skip whitespace
|
||||
*/
|
||||
if (!(U_INCLUDE_WS & _P_flags) && isspace(*_P_nextchr))
|
||||
{
|
||||
_P_nextchr++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
** check to see if this character starts a comment
|
||||
*/
|
||||
if ((comptr = W_iscom(_P_nextchr)) != W_COMNULL)
|
||||
{
|
||||
if (_P_comsnarf(comptr))
|
||||
{
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
** if there aren't any tokens on this line already
|
||||
** set up the index from the token line to the content line
|
||||
*/
|
||||
if (!L_getcount(_P_fnumb,L_gettlmax(_P_fnumb)))
|
||||
{
|
||||
L_settlindex(_P_fnumb,
|
||||
L_gettlmax(_P_fnumb),
|
||||
L_getclmax(_P_fnumb));
|
||||
/*
|
||||
** and the pointer from the token line to the
|
||||
** first token on the line
|
||||
*/
|
||||
L_setindex(_P_fnumb,
|
||||
L_gettlmax(_P_fnumb),
|
||||
K_gettmax(_P_fnumb));
|
||||
}
|
||||
|
||||
startline = L_tl2cl(_P_fnumb,L_gettlmax(_P_fnumb));
|
||||
startpos = _P_nextchr-_P_firstchr;
|
||||
|
||||
newtoken = K_maketoken();
|
||||
K_setline(newtoken,L_gettlmax(_P_fnumb));
|
||||
K_setpos(newtoken,startpos);
|
||||
|
||||
ret_code = 0;
|
||||
/*
|
||||
** check to see if this character starts a
|
||||
** delimited literal string
|
||||
*/
|
||||
if ((litptr = W_islit(_P_nextchr)) != W_LITNULL)
|
||||
{
|
||||
ret_code = _P_litsnarf(litptr);
|
||||
K_settype(newtoken,K_LIT);
|
||||
S_allocstr(&ptr,_P_stringsize);
|
||||
/*
|
||||
** fixed nasty memory bug here by adding else
|
||||
** old code copied entire line even if literal
|
||||
** ended before the end of line
|
||||
** should check into getting strcpy loaded
|
||||
** locally
|
||||
*/
|
||||
endline = L_getclmax(_P_fnumb);
|
||||
if (endline > startline)
|
||||
{
|
||||
/*
|
||||
** copy in the first line of the literal
|
||||
*/
|
||||
(void) strcpy(ptr,
|
||||
L_getcline(_P_fnumb,startline)
|
||||
+startpos);
|
||||
/*
|
||||
** now copy all the lines between
|
||||
** the first and last
|
||||
*/
|
||||
for (tmp=startline+1;tmp<endline;tmp++)
|
||||
{
|
||||
(void) strcat(ptr,
|
||||
L_getcline(_P_fnumb,tmp));
|
||||
}
|
||||
/*
|
||||
** and now copy in the last line
|
||||
*/
|
||||
(void) strncat(ptr,
|
||||
L_getcline(_P_fnumb,endline),
|
||||
_P_stringsize-strlen(ptr));
|
||||
}
|
||||
else
|
||||
{
|
||||
(void) strncpy(ptr,
|
||||
L_getcline(_P_fnumb,startline)
|
||||
+startpos,
|
||||
_P_stringsize);
|
||||
/*
|
||||
** terminate the string you just copied
|
||||
*/
|
||||
ptr[_P_stringsize] = '\0';
|
||||
}
|
||||
K_settext(newtoken,ptr);
|
||||
} /* if is_lit */
|
||||
|
||||
/*
|
||||
** see if this is a floating point number
|
||||
*/
|
||||
else if (tmp = F_isfloat(_P_nextchr,
|
||||
_P_flags & U_NEED_DECIMAL,
|
||||
_P_flags & U_INC_SIGN))
|
||||
{
|
||||
K_saventext(newtoken,_P_nextchr,tmp);
|
||||
K_settype(newtoken,K_FLO_NUM);
|
||||
if (!(_P_flags & U_BYTE_COMPARE))
|
||||
{
|
||||
K_setfloat(newtoken,
|
||||
F_atof(K_gettext(newtoken),
|
||||
USE_ALL));
|
||||
|
||||
/*
|
||||
** assign the curent tolerance
|
||||
*/
|
||||
K_settol(newtoken,T_gettol(_P_next_tol));
|
||||
}
|
||||
|
||||
/*
|
||||
** use next tolerance in the
|
||||
** specification if there is one
|
||||
*/
|
||||
if (T_moretols(_P_next_tol))
|
||||
{
|
||||
_P_next_tol++;
|
||||
}
|
||||
/*
|
||||
** and move pointer past the float
|
||||
*/
|
||||
_P_nextchr += tmp;
|
||||
}
|
||||
|
||||
/*
|
||||
** is this a fixed point number
|
||||
*/
|
||||
else if (isdigit(*_P_nextchr))
|
||||
{
|
||||
for(ptr=_P_nextchr; isdigit(*ptr); ptr++)
|
||||
{
|
||||
}
|
||||
K_saventext(newtoken,_P_nextchr,ptr-_P_nextchr);
|
||||
K_settype(newtoken,K_LIT);
|
||||
_P_nextchr = ptr;
|
||||
}
|
||||
|
||||
/*
|
||||
** try an alpha-numeric word
|
||||
*/
|
||||
else if (isalpha(*_P_nextchr) || _P_in_alpha(*_P_nextchr))
|
||||
{
|
||||
/*
|
||||
** it's a multi character word
|
||||
*/
|
||||
for(ptr = _P_nextchr;
|
||||
isalpha(*ptr)
|
||||
|| isdigit(*ptr)
|
||||
|| _P_in_alpha(*ptr);
|
||||
ptr++)
|
||||
{
|
||||
}
|
||||
K_saventext(newtoken,_P_nextchr,ptr-_P_nextchr);
|
||||
K_settype(newtoken,K_LIT);
|
||||
_P_nextchr = ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
** otherwise, treat the char itself as a token
|
||||
*/
|
||||
K_saventext(newtoken,_P_nextchr,1);
|
||||
K_settype(newtoken,K_LIT);
|
||||
_P_nextchr++;
|
||||
}
|
||||
|
||||
K_settoken(_P_fnumb,K_gettmax(_P_fnumb),newtoken);
|
||||
L_inccount(_P_fnumb,L_gettlmax(_P_fnumb));
|
||||
/*
|
||||
** if we are out of space, complain and quit
|
||||
*/
|
||||
if (K_inctmax(_P_fnumb))
|
||||
{
|
||||
(void) sprintf(Z_err_buf,
|
||||
"warning -- to many tokens in file only first %d tokens will be used.\n",
|
||||
K_MAXTOKENS);
|
||||
Z_complain(Z_err_buf);
|
||||
return;
|
||||
}
|
||||
#ifndef NOCHATTER
|
||||
if (0 == (K_gettmax(_P_fnumb) % _P_PARSE_CHATTER))
|
||||
{
|
||||
int max = K_gettmax(_P_fnumb);
|
||||
(void) sprintf(Z_err_buf,
|
||||
"scanned %d words from file #%d\n",
|
||||
max,_P_fnumb+1);
|
||||
Z_chatter(Z_err_buf);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** are we done?
|
||||
*/
|
||||
if(ret_code)
|
||||
{
|
||||
return;
|
||||
}
|
||||
} /* loop once per object on a line */
|
||||
|
||||
#ifndef lint
|
||||
Z_fatal("this line should never execute");
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
P_file_parse(num,strt,lcnt,flags)
|
||||
int num; /* file number */
|
||||
int strt; /* first line to parse expressed in real line numbers */
|
||||
int lcnt; /* max number of lines to parse */
|
||||
int flags; /* flags for controlling the parse mode */
|
||||
{
|
||||
/*
|
||||
** set module-wide state variables
|
||||
*/
|
||||
_P_fnumb = num;
|
||||
_P_start = strt;
|
||||
_P_lcount = lcnt;
|
||||
_P_flags = flags;
|
||||
|
||||
_P_initparser();
|
||||
|
||||
_P_nextchr = _P_dummyline;
|
||||
|
||||
_P_has_content = 0;
|
||||
_P_next_tol = 0;
|
||||
L_setcount(_P_fnumb,L_gettlmax(_P_fnumb),0);
|
||||
/*
|
||||
** start everything back one line (it will be incremented
|
||||
** just before the first line is accessed
|
||||
*/
|
||||
_P_realline = _P_start-1;
|
||||
|
||||
_P_do_parse();
|
||||
|
||||
/*
|
||||
** if the last line had content, increment the count
|
||||
*/
|
||||
if (_P_has_content)
|
||||
{
|
||||
/*
|
||||
** this code will get executed if we stopped parsing in the middle
|
||||
** of a line. i haven't looked at this case carefully.
|
||||
** so, there is a good chance that it is buggy.
|
||||
*/
|
||||
(void) sprintf(Z_err_buf,"parser got confused at end of file\n");
|
||||
Z_complain(Z_err_buf);
|
||||
L_incclmax(_P_fnumb);
|
||||
if (L_getcount(_P_fnumb,L_gettlmax(_P_fnumb)))
|
||||
L_inctlmax(_P_fnumb);
|
||||
}
|
||||
return;
|
||||
}
|
10
utils/Spiff/parse.h
Normal file
10
utils/Spiff/parse.h
Normal file
@ -0,0 +1,10 @@
|
||||
/* Copyright (c) 1988 Bellcore
|
||||
** All Rights Reserved
|
||||
** Permission is granted to copy or use this program, EXCEPT that it
|
||||
** may not be sold for profit, the copyright notice must be reproduced
|
||||
** on copies, and credit should be given to Bellcore where it is due.
|
||||
** BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
|
||||
*/
|
||||
|
||||
extern void P_file_parse();
|
||||
extern void P_addalpha();
|
454
utils/Spiff/spiff.1
Normal file
454
utils/Spiff/spiff.1
Normal file
@ -0,0 +1,454 @@
|
||||
.ll 6i
|
||||
.pl 10.5i
|
||||
.po 1.25i
|
||||
.\" @(#)spiff.1 1.0 (Bellcore) 9/20/87
|
||||
.\"
|
||||
.lt 6.0i
|
||||
.TH SPIFF 1 "February 2, 1988"
|
||||
.AT 3
|
||||
.SH NAME
|
||||
spiff \- make controlled approximate comparisons between files
|
||||
.SH SYNOPSIS
|
||||
.B spiff
|
||||
[
|
||||
.B \-s
|
||||
script ] [
|
||||
.B \-f
|
||||
sfile ] [
|
||||
.B \-bteviqcdwm
|
||||
] [
|
||||
.B \-a
|
||||
\(br
|
||||
.B \-r
|
||||
value ] \-value file1 file2
|
||||
.SH DESCRIPTION
|
||||
.I Spiff
|
||||
compares the contents of
|
||||
.B file1
|
||||
and
|
||||
.B file2
|
||||
and prints a description of the important differences between
|
||||
the files.
|
||||
White space is ignored except to separate other objects.
|
||||
.I Spiff
|
||||
maintains tolerances below which differences between two floating point
|
||||
numbers are ignored.
|
||||
Differences in floating point notation (such as 3.4 3.40 and 3.4e01)
|
||||
are treated as unimportant.
|
||||
User specified delimited strings (i.e. comments) can also be ignored.
|
||||
Inside other user specified delimited strings
|
||||
(i.e. quoted strings) whitespace can be significant.
|
||||
.PP
|
||||
.I Spiff's
|
||||
operation can be altered via command line options, a command script, and with
|
||||
commands that are embedded in the input files.
|
||||
.PP
|
||||
The following options affect
|
||||
.I spiff's
|
||||
overall operation.
|
||||
.TP
|
||||
.B \-q
|
||||
suppresses warning messages.
|
||||
.TP
|
||||
.B \-v
|
||||
use a visually oriented display. Works only in MGR windows.
|
||||
.PP
|
||||
.I Spiff
|
||||
has several flags to aid differencing of various programming languages.
|
||||
See EXAMPLES for a detailed description of the effects of these flags.
|
||||
.TP
|
||||
.B \-C
|
||||
treat the input files as C program source code.
|
||||
.TP
|
||||
.B \-S
|
||||
treat the input files as Bourne shell program source code.
|
||||
.TP
|
||||
.B \-F
|
||||
treat the input files as Fortran program source code.
|
||||
.TP
|
||||
.B \-M
|
||||
treat the input files as Modula-2 program source code.
|
||||
.TP
|
||||
.B \-L
|
||||
treat the input files as Lisp program source code.
|
||||
.PP
|
||||
By default, the output looks somewhat similar in appearance
|
||||
to the output of diff(1). Lines with differences are printed with
|
||||
the differences highlighted. If stdout is a terminal, as determined
|
||||
by isatty(), then highlighting uses standout mode as determined by termcap.
|
||||
If stdout is not a tty, then the underlining (via underscore/backspace/char)
|
||||
is used to highlight differences.
|
||||
The following option can control the format of the ouput.
|
||||
.TP
|
||||
.B \-t
|
||||
produce output in terms of individual tokens. This option is
|
||||
most useful for debugging as the output produced is verbose to
|
||||
the point of being unreadable.
|
||||
.PP
|
||||
The following option controls the differencing algorithm.
|
||||
.TP
|
||||
.B \-e
|
||||
compare each token
|
||||
in the files with the object in the same ordinal
|
||||
position in the other file. If the files have a different number
|
||||
of objects, a warning message is printed
|
||||
and the objects at the end of the longer file are ignored.
|
||||
By default,
|
||||
.I spiff
|
||||
uses a Miller/Myers algorithm to find a minimal edit sequence
|
||||
that will convert the contents of the first file into the second.
|
||||
.TP
|
||||
\-<decimal-value>
|
||||
sets a limit on the total number of insertions and deletions
|
||||
that will be considered.
|
||||
If the files differ by more than the stated amount,
|
||||
the program will give up, print a warning message, and exit.
|
||||
.PP
|
||||
The following options control the command script. More than one of
|
||||
each may appear at at time. The commands accumulate.
|
||||
.TP
|
||||
.B \-f sfile
|
||||
a command script to be taken from file
|
||||
.IR sfile
|
||||
.TP
|
||||
.B \-s command-script
|
||||
causes the following argument to be taken as a command script.
|
||||
.PP
|
||||
The following options control how individual objects are compared.
|
||||
.TP
|
||||
.B \-b
|
||||
treat all objects (including floating point numbers) as literals.
|
||||
.TP
|
||||
.B \-c
|
||||
ignore differences between upper and lower case.
|
||||
.PP
|
||||
The following commands will control how the files are parsed.
|
||||
.TP
|
||||
.B \-w
|
||||
treat white space as objects. Each white space character will
|
||||
be treated as a separate object when the program is comparing the
|
||||
files.
|
||||
.TP
|
||||
.B \-m
|
||||
treat leading sign characters ( + and \- ) as separate even
|
||||
if they are followed by floating point numbers.
|
||||
.TP
|
||||
.B \-d
|
||||
treat integer decimal numbers (such as 1987) as real numbers (subject to
|
||||
tolerances) rather than as literal strings.
|
||||
.PP
|
||||
The following three flags are used to set the default tolerances.
|
||||
The floating-point-numbers may be given in the formats accepted
|
||||
by atof(3).
|
||||
.TP
|
||||
.B \-a floating-point-number
|
||||
specifies an absolute value for the tolerance in floating point numbers.
|
||||
The flag
|
||||
.B \-a1e-2
|
||||
will cause all differences greater than 0.01 to be reported.
|
||||
.TP
|
||||
.B \-r floating-point-number
|
||||
specifies a relative tolerance. The value given is interpreted
|
||||
as a fraction of the larger (in absolute terms)
|
||||
of the two floating point numbers being compared.
|
||||
Thus, the flag
|
||||
.B \-r0.1
|
||||
will cause the two floating point numbers 1.0 and 0.9 to be deemed within
|
||||
tolerance. The numbers 1.0 and 0.89 will be outside the tolerance.
|
||||
.TP
|
||||
.B \-i
|
||||
causes differences between floating point numbers to be ignored.
|
||||
.PP
|
||||
If more than one
|
||||
.B \-a, \-r,
|
||||
or
|
||||
.B \-i
|
||||
flag appear on the command line,
|
||||
the tolerances will be OR'd together (i.e. any difference that is within
|
||||
any of the tolerances will be ignored).
|
||||
.PP
|
||||
If no default tolerances is set on the command line,
|
||||
the program will use a default tolerance of
|
||||
.B '\-a 1e-10 \-r 1e-10'.
|
||||
.SH SCRIPT COMMANDS
|
||||
.PP
|
||||
A script consists of commands, one per line.
|
||||
Each command consists of a keyword possibly followed by arguments.
|
||||
Arguments are separated by one or more tabs or spaces.
|
||||
The commands are:
|
||||
.TP
|
||||
literal BEGIN-STRING [END-STRING [ESCAPE-STRING]]
|
||||
Specifies the delimiters surrounding text that is to be treated as a single
|
||||
literal object. If only one argument is present, then only that string itself is treated
|
||||
as a literal. If only two arguments are present, they are taking as the starting
|
||||
and ending delimiters respectively. If three arguments are present, they are treated
|
||||
as the start delimiter, end delimiter, and a string that may be used to escape
|
||||
an instance of the end delimiter.
|
||||
.TP
|
||||
beginchar BEGINNING-OF-LINE-CHARACTER
|
||||
Set the the beginning of line character for BEGIN-STRING's in comments.
|
||||
The default is '^'.
|
||||
.TP
|
||||
endchar END-OF-LINE-CHARACTER
|
||||
Set the end of line character for END-STRING's in comments.
|
||||
The default is '$'.
|
||||
.TP
|
||||
addalpha NEW-ALPHA-CHARACTER
|
||||
Add NEW-ALPHA-CHARACTER to the set of characters allowed in literal strings.
|
||||
By default,
|
||||
.I spiff
|
||||
parses sequences of characters that begin with a letter and followed by
|
||||
zero or more letters or numbers as a single literal token. This definition
|
||||
is overly restrictive when dealing with programming languages.
|
||||
For example, in the C programming language,
|
||||
the underscore character is allowed in identifiers.
|
||||
.TP
|
||||
comment BEGIN-STRING [END-STRING [ESCAPE-STRING]]
|
||||
Specifies the delimiters surrounding text that is to be be ignored entirely
|
||||
(i.e. viewed as comments).
|
||||
The operation of the comment command is very similar to the literal command.
|
||||
In addition, if the END-STRING consists of only
|
||||
the end of line character, the end of line will delimit the end of the comment.
|
||||
Also, if the BEGIN-STRING starts with the beginning of line character, only
|
||||
lines that begin with the BEGIN-STRING will be ignored.
|
||||
.PP
|
||||
More than one comment specification and more than one literal string specification
|
||||
may be specified at a time.
|
||||
.TP
|
||||
nestcom BEGIN-STRING [END-STRING [ESCAPE-STRING]]
|
||||
Similar to the comment command, but allows comments to be nested.
|
||||
Note, due to the design of the parser nested comments can not
|
||||
have a BEGIN-STRING that starts with the beginning of line character.
|
||||
.TP
|
||||
resetcomments
|
||||
Clears the list of comment specifications.
|
||||
.TP
|
||||
resetliterals
|
||||
Clears the list of literal specifications.
|
||||
.TP
|
||||
tol [aVALUE\(brrVALUE\(bri\(brd . . . [ ; aVALUE\(brrVALUE\(bri\(brd . . . ] . . . ]
|
||||
set the tolerance for floating point comparisons.
|
||||
The arguments to the tol command are a set of tolerance specifications
|
||||
separated by semicolons. If more than one a,r,d, or i appears within
|
||||
a specification, then the tolerances are OR'd together (i.e. any difference
|
||||
that is within any tolerance will be ignored).
|
||||
The semantics of a,r, and i are identical to the
|
||||
.B \-a, \-r,
|
||||
and
|
||||
.B \-i
|
||||
flags. The d means that the default tolerance (as specified by the invocation
|
||||
options) should be used.
|
||||
If more than one specification appears on the line, the first
|
||||
specification is applied to the first floating point number on each line,
|
||||
the second specification to the second floating point number one each line
|
||||
of the input files, and so on. If there are more floating point numbers
|
||||
on a given line of input than tolerance specifications,
|
||||
the last specification is used repeatedly for all remaining floating point numbers
|
||||
on that line.
|
||||
.TP
|
||||
command STRING
|
||||
lines in the input file that start with STRING will be interpreted as
|
||||
command lines. If no "command" is given as part of a
|
||||
.B \-s
|
||||
or
|
||||
.B \-f
|
||||
then it will be impossible to embed commands in the input files.
|
||||
.TP
|
||||
rem
|
||||
.TP
|
||||
#
|
||||
used to places human readable remarks into a commands script. Note that the
|
||||
use of the '#' character differs from other command languages (for instance
|
||||
the Bourne shell).
|
||||
.I Spiff
|
||||
will only recognize the '#' as beginning a comment when it is the first
|
||||
non-blank character on the command line. A '#' character appearing elsewhere
|
||||
will be treated as part of the command. Cautious users should use 'rem'.
|
||||
Those hopelessly addicted to '#' as a comment character can have command
|
||||
scripts with a familiar format.
|
||||
.PP
|
||||
Tolerances specified in the command scripts have precedence over the tolerance
|
||||
specified on the invocation command line. The tolerance specified in
|
||||
.I file1
|
||||
has precedence over the tolerance specified in
|
||||
.I file2.
|
||||
.PP
|
||||
.SH VISUAL MODE
|
||||
If
|
||||
.I spiff
|
||||
is invoked with the \-v option, it will enter an interactive mode rather
|
||||
than produce an edit sequence. Three windows will be put on the screen.
|
||||
Two windows will contain corresponding segments of the input files.
|
||||
Objects that appear in both segments will be examined for differences and
|
||||
if any difference is found, the objects will be highlighted in reverse video
|
||||
on the screen. Objects that appear in only one window will have a line drawn
|
||||
through them to indicate that they aren't being compared with anything in the other
|
||||
text window. The third window is a command window. The command window will
|
||||
accept a single tolerance specification (followed by a newline)
|
||||
in a form suitable to the
|
||||
.B tol
|
||||
command. The tolerance specified will then be used as the default tolerance
|
||||
and the display will be updated to highlight only those objects that exceed
|
||||
the new default tolerance. Typing
|
||||
.B m
|
||||
(followed by a newline) will display the next screenfull of text. Typing
|
||||
.B q
|
||||
(followed by a newline) will cause the program to exit.
|
||||
.SH LIMITS
|
||||
Each input files can be no longer that 10,000 line long or contain more
|
||||
than 50,000 tokens. Longer files will be truncated.
|
||||
No line can be longer than 1024 characters. Newlines
|
||||
will be inserted every 1024 character.
|
||||
.SH EXAMPLES
|
||||
.TP
|
||||
spiff \-e \-d foo bar
|
||||
this invocation (using exact match algorithm and treating integer numbers
|
||||
as if they were floats) is very useful for examining large tables of numbers.
|
||||
.TP
|
||||
spiff \-0 foo bar
|
||||
compare the two files, quitting after the first difference is found.
|
||||
This makes the program operate roughly like cmp(1).
|
||||
.TP
|
||||
spiff \-0 -q foo bar
|
||||
same as the above, but no output is produced.
|
||||
The return code is still useful.
|
||||
.TP
|
||||
spiff \-w \-b foo bar
|
||||
will make the program operate much like diff(1).
|
||||
.TP
|
||||
spiff \-a1e-5 \-r0.001 foo bar
|
||||
compare the contents of the files foo and bar and ignore all differences between
|
||||
floating point numbers that are less than or equal to
|
||||
0.00001 or 0.1% of the number of larger magnitude.
|
||||
.TP
|
||||
tol a.01 r.01
|
||||
will cause all differences between floating point numbers that are less than
|
||||
or equal to
|
||||
0.01 or 1% of the number of larger magnitude to be ignored.
|
||||
.TP
|
||||
tol a.01 r.01 ; i
|
||||
will cause the tolerance in the previous example to be applied to the first
|
||||
floating point number on each line. All differences between the second and
|
||||
subsequent floating point numbers on each line will be ignored.
|
||||
.TP
|
||||
tol a.01 r.01 ; i ; a.0001
|
||||
like the above except that only differences between the second floating point
|
||||
number on each line will be ignored. The differences between
|
||||
third and subsequent floating point numbers on each number will be ignored if they
|
||||
are less than or equal to 0.0001.
|
||||
.IP
|
||||
A useful script for examing C code is:
|
||||
.nf
|
||||
literal " " \\
|
||||
comment /* */
|
||||
literal &&
|
||||
literal \(br\(br
|
||||
literal <=
|
||||
literal >=
|
||||
literal !=
|
||||
literal ==
|
||||
literal --
|
||||
literal ++
|
||||
literal <<
|
||||
literal >>
|
||||
literal ->
|
||||
addalpha _
|
||||
tol a0
|
||||
.fi
|
||||
.IP
|
||||
A useful script for shell programs is:
|
||||
.nf
|
||||
literal ' ' \\
|
||||
comment # $
|
||||
tol a0
|
||||
.fi
|
||||
.IP
|
||||
A useful script for Fortran programs is:
|
||||
.nf
|
||||
literal ' ' '
|
||||
comment ^C $
|
||||
tol a0
|
||||
.fi
|
||||
.IP
|
||||
A useful script for Modula 2 programs is:
|
||||
.nf
|
||||
literal ' '
|
||||
literal " "
|
||||
nestcom (* *)
|
||||
literal :=
|
||||
literal <>
|
||||
literal <=
|
||||
literal >=
|
||||
tol a0
|
||||
.fi
|
||||
.IP
|
||||
A useful script for Lisp programs is:
|
||||
.nf
|
||||
literal " "
|
||||
comment ; $
|
||||
tol a0
|
||||
.fi
|
||||
.SH DIAGNOSTICS
|
||||
.I Spiff's
|
||||
exit status is 0 if no differences are found, 1 if differences are found, and
|
||||
2 upon error.
|
||||
.SH BUGS
|
||||
In C code, escaped newlines will appear as differences.
|
||||
.PP
|
||||
Comments are treated as token delimiters.
|
||||
.PP
|
||||
Comments in Basic don't work right. The line number is not ignored.
|
||||
.PP
|
||||
Continuation lines in Fortran comments don't work.
|
||||
.PP
|
||||
There is no way to represent strings specified using a
|
||||
Hollerith notation in Fortran.
|
||||
.PP
|
||||
In formated English text, hyphenated words,
|
||||
movements in pictures, footnotes, etc.
|
||||
will be reported as differences.
|
||||
.PP
|
||||
STRING's in script commands can not include whitespace.
|
||||
.PP
|
||||
Visual mode does not handle tabs properly. Files containing
|
||||
tabs should be run through
|
||||
expand(1) before trying to display them with visual mode.
|
||||
.PP
|
||||
In visual mode, the text windows appear in a fixed size and font.
|
||||
Lines longer than the window size will not be handled properly.
|
||||
.PP
|
||||
Objects (literal strings) that contain newlines cause trouble in several places
|
||||
in visual mode.
|
||||
.PP
|
||||
Visual mode should accept more than one tolerance specification.
|
||||
.PP
|
||||
When using visual mode or the exact match comparison algorithm, the program
|
||||
should do the parsing on the fly rather than truncating long files.
|
||||
.SH AUTHOR
|
||||
Daniel Nachbar
|
||||
.SH COPYRIGHT
|
||||
.nf
|
||||
Copyright (c) 1988 Bellcore
|
||||
All Rights Reserved
|
||||
Permission is granted to copy or use this program,
|
||||
EXCEPT that it may not be sold for profit, the copyright
|
||||
notice must be reproduced on copies, and credit should
|
||||
be given to Bellcore where it is due.
|
||||
BELLCORE MAKES NO WARRANTY AND ACCEPTS
|
||||
NO LIABILITY FOR THIS PROGRAM.
|
||||
.fi
|
||||
|
||||
.br
|
||||
.SH SEE ALSO
|
||||
atof(3)
|
||||
isatty(2)
|
||||
diff(1)
|
||||
cmp(1)
|
||||
expand(1)
|
||||
mgr(1L)
|
||||
.PP
|
||||
"Spiff -- A Program for Making Controlled Approximate Comparisons of Files",
|
||||
by Daniel Nachbar.
|
||||
.PP
|
||||
"A File Comparison Program" by Webb Miller and Eugene W. Myers in Software \-
|
||||
Practice and Experience, Volume 15(11), pp.1025-1040, (November 1985).
|
341
utils/Spiff/spiff.c
Normal file
341
utils/Spiff/spiff.c
Normal file
@ -0,0 +1,341 @@
|
||||
/* Copyright (c) 1988 Bellcore
|
||||
** All Rights Reserved
|
||||
** Permission is granted to copy or use this program, EXCEPT that it
|
||||
** may not be sold for profit, the copyright notice must be reproduced
|
||||
** on copies, and credit should be given to Bellcore where it is due.
|
||||
** BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef lint
|
||||
static char rcsid[]= "$Header$";
|
||||
#endif
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include "misc.h"
|
||||
#include "flagdefs.h"
|
||||
#include "parse.h"
|
||||
#include "edit.h"
|
||||
#include "line.h"
|
||||
#include "token.h"
|
||||
#include "tol.h"
|
||||
#include "command.h"
|
||||
#include "compare.h"
|
||||
#include "exact.h"
|
||||
#include "miller.h"
|
||||
#include "visual.h"
|
||||
#include "output.h"
|
||||
|
||||
extern void _Y_doargs();
|
||||
|
||||
static int _Y_eflag = 0; /* use exact match algorithm */
|
||||
static int _Y_vflag = 0; /* use visual mode */
|
||||
|
||||
/*
|
||||
** this is the set of flags that gets used throughout the top module
|
||||
** as well as being used to communicate between modules.
|
||||
*/
|
||||
static int _Y_flags;
|
||||
|
||||
main(argc,argv)
|
||||
int argc;
|
||||
char *argv[];
|
||||
{
|
||||
E_edit edit_end;
|
||||
char *filename[2];
|
||||
|
||||
int max_d; /* max number of differences allowed */
|
||||
int i; /* loop counter */
|
||||
|
||||
/*
|
||||
** parse the command line
|
||||
*/
|
||||
_Y_doargs(argc,argv,&(filename[0]),&(filename[1]),&max_d);
|
||||
|
||||
/*
|
||||
** initialize the default tolerance if it
|
||||
** hasn't been set already.
|
||||
*/
|
||||
T_initdefault();
|
||||
|
||||
/*
|
||||
** read and then parse the files
|
||||
*/
|
||||
|
||||
/*
|
||||
** L_initfile return a code that indicates if the
|
||||
** entire file was read or not
|
||||
**
|
||||
** P_fileparse also knows how to start at someplace other
|
||||
** than the first line of file
|
||||
**
|
||||
** Taken together, this is enough to do step our way
|
||||
** through the file using an exact match algorithm.
|
||||
**
|
||||
** Oh well, someday . . .
|
||||
*/
|
||||
for(i=0;i<=1;i++)
|
||||
{
|
||||
/*
|
||||
** read the file into core
|
||||
*/
|
||||
(void) L_init_file(i,filename[i]);
|
||||
K_settmax(i,0); /* start tokens at 0 */
|
||||
/*
|
||||
** and parse the files into tokens
|
||||
*/
|
||||
P_file_parse(i,0,L_getrlmax(i),_Y_flags);
|
||||
}
|
||||
|
||||
if (_Y_vflag)
|
||||
{
|
||||
return(V_visual(_Y_flags));
|
||||
}
|
||||
|
||||
/*
|
||||
** if max_d was not set on the command line
|
||||
** set it to be as large as is possible
|
||||
** since the most changes possible would
|
||||
** be to delete all the tokens in the
|
||||
** first file and add all the tokens from
|
||||
** the second, the max possible is the
|
||||
** sum of the number of tokens in the
|
||||
** two files.
|
||||
*/
|
||||
if (-1 == max_d)
|
||||
max_d = K_gettmax(0) + K_gettmax(1);
|
||||
|
||||
if (_Y_eflag)
|
||||
{
|
||||
edit_end = Q_do_exact(K_gettmax(0),K_gettmax(1),
|
||||
max_d,_Y_flags);
|
||||
}
|
||||
else
|
||||
{
|
||||
edit_end = G_do_miller(K_gettmax(0), K_gettmax(1),
|
||||
max_d,_Y_flags);
|
||||
}
|
||||
|
||||
if (E_NULL != edit_end)
|
||||
{
|
||||
O_output(edit_end,_Y_flags);
|
||||
return(1);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
** break a string into individual lines and feed
|
||||
** them to the command module
|
||||
*/
|
||||
static void
|
||||
_Y_cmdlines(from)
|
||||
char *from;
|
||||
{
|
||||
char buf[Z_LINELEN];
|
||||
char *to;
|
||||
while ('\0' != *from)
|
||||
{
|
||||
/*
|
||||
** copy line into buf
|
||||
*/
|
||||
to = buf;
|
||||
while (('\0' != *from) && ('\n' != *from))
|
||||
{
|
||||
*to++ = *from++;
|
||||
}
|
||||
*to = '\0'; /* terminate the line */
|
||||
|
||||
/*
|
||||
** hand the line to the command module
|
||||
*/
|
||||
C_addcmd(buf);
|
||||
/*
|
||||
** skip the newline
|
||||
*/
|
||||
if ('\n' == *from)
|
||||
{
|
||||
from++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** this useful macro handle arguements that are adjacent
|
||||
** to a flag or in the following word e.g --
|
||||
**
|
||||
** -a XXX
|
||||
** and
|
||||
** -aXXX
|
||||
**
|
||||
** both work when SETPTR is used.
|
||||
*/
|
||||
#define SETPTR {if(strlen(argv[1]) == 2) {argv++;argc--;ptr=argv[1];}else ptr=(&argv[1][2]);}
|
||||
|
||||
static void
|
||||
_Y_doargs(argc,argv,file1,file2,max_d)
|
||||
int argc;
|
||||
char *argv[];
|
||||
char **file1,**file2;
|
||||
int *max_d;
|
||||
{
|
||||
char *ptr;
|
||||
|
||||
/*
|
||||
** mark maximum number of tokens as being unset
|
||||
*/
|
||||
*max_d = -1;
|
||||
|
||||
while (argc > 1 && argv[1][0] == '-')
|
||||
{
|
||||
switch (argv[1][1])
|
||||
{
|
||||
case 't':
|
||||
_Y_flags |= U_TOKENS;
|
||||
break;
|
||||
case 'w':
|
||||
_Y_flags |= U_INCLUDE_WS;
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
_Y_flags |= U_BYTE_COMPARE;
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
_Y_flags |= U_NO_CASE;
|
||||
break;
|
||||
case 'd' :
|
||||
_Y_flags |= U_NEED_DECIMAL;
|
||||
break;
|
||||
case 'm' :
|
||||
_Y_flags |= U_INC_SIGN;
|
||||
break;
|
||||
case 'a':
|
||||
SETPTR;
|
||||
T_defatol(ptr);
|
||||
break;
|
||||
case 'r':
|
||||
SETPTR;
|
||||
T_defrtol(ptr);
|
||||
break;
|
||||
case 'i':
|
||||
T_defitol();
|
||||
break;
|
||||
case 'e' :
|
||||
_Y_eflag = 1;
|
||||
break;
|
||||
case 'v' :
|
||||
_Y_vflag = 1;
|
||||
break;
|
||||
case 'q' :
|
||||
Z_setquiet();
|
||||
break;
|
||||
case 's' :
|
||||
SETPTR;
|
||||
_Y_cmdlines(ptr);
|
||||
break;
|
||||
case 'f' :
|
||||
{
|
||||
extern FILE *fopen();
|
||||
char buf[Z_LINELEN];
|
||||
FILE *cmdfile;
|
||||
SETPTR;
|
||||
if ((FILE*) NULL ==
|
||||
(cmdfile = fopen(ptr,"r")))
|
||||
{
|
||||
Z_fatal("can't open command file\n");
|
||||
}
|
||||
while ((char*) NULL !=
|
||||
(char*) fgets(buf,Z_LINELEN,cmdfile))
|
||||
{
|
||||
C_addcmd(buf);
|
||||
}
|
||||
(void) fclose(cmdfile);
|
||||
break;
|
||||
}
|
||||
/*
|
||||
** useful commands for
|
||||
** the C programming language
|
||||
*/
|
||||
case 'C' :
|
||||
C_addcmd("literal \" \" \\ ");
|
||||
C_addcmd("comment /* */ ");
|
||||
C_addcmd("literal && ");
|
||||
C_addcmd("literal || ");
|
||||
C_addcmd("literal <= ");
|
||||
C_addcmd("literal >= ");
|
||||
C_addcmd("literal != ");
|
||||
C_addcmd("literal == ");
|
||||
C_addcmd("literal -- ");
|
||||
C_addcmd("literal ++ ");
|
||||
C_addcmd("literal << ");
|
||||
C_addcmd("literal >> ");
|
||||
C_addcmd("literal -> ");
|
||||
C_addcmd("addalpha _ ");
|
||||
C_addcmd("tol a0 ");
|
||||
break;
|
||||
/*
|
||||
** useful commands for
|
||||
** the Bourne shell programming language
|
||||
*/
|
||||
case 'S' :
|
||||
C_addcmd("literal ' ' \\ ");
|
||||
C_addcmd("comment # $ ");
|
||||
C_addcmd("tol a0 ");
|
||||
break;
|
||||
/*
|
||||
** useful commands for
|
||||
** the Fortran programming language
|
||||
*/
|
||||
case 'F' :
|
||||
C_addcmd("literal ' ' ' ");
|
||||
C_addcmd("comment ^C $ ");
|
||||
C_addcmd("tol a0 ");
|
||||
break;
|
||||
/*
|
||||
** useful commands for
|
||||
** the Lisp programming language
|
||||
*/
|
||||
case 'L' :
|
||||
C_addcmd("literal \" \" ");
|
||||
C_addcmd("comment ; $ ");
|
||||
C_addcmd("tol a0 ");
|
||||
break;
|
||||
/*
|
||||
** useful commands for
|
||||
** the Modula-2 programming language
|
||||
*/
|
||||
case 'M' :
|
||||
C_addcmd("literal ' ' ");
|
||||
C_addcmd("literal \" \" ");
|
||||
C_addcmd("comment (* *) ");
|
||||
C_addcmd("literal := ");
|
||||
C_addcmd("literal <> ");
|
||||
C_addcmd("literal <= ");
|
||||
C_addcmd("literal >= ");
|
||||
C_addcmd("tol a0 ");
|
||||
break;
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
*max_d = atoi(&argv[1][1]);
|
||||
break;
|
||||
default:
|
||||
Z_fatal("don't understand arguments\n");
|
||||
}
|
||||
++argv;
|
||||
--argc;
|
||||
}
|
||||
if (argc != 3)
|
||||
Z_fatal ("spiff requires two file names.\n");
|
||||
*file1 = argv[1];
|
||||
*file2 = argv[2];
|
||||
}
|
162
utils/Spiff/strings.c
Normal file
162
utils/Spiff/strings.c
Normal file
@ -0,0 +1,162 @@
|
||||
/* Copyright (c) 1988 Bellcore
|
||||
** All Rights Reserved
|
||||
** Permission is granted to copy or use this program, EXCEPT that it
|
||||
** may not be sold for profit, the copyright notice must be reproduced
|
||||
** on copies, and credit should be given to Bellcore where it is due.
|
||||
** BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef lint
|
||||
static char rcsid[]= "$Header$";
|
||||
#endif
|
||||
|
||||
#include <ctype.h>
|
||||
#include "misc.h"
|
||||
#include "strings.h"
|
||||
|
||||
/*
|
||||
** routines for handling strings.
|
||||
** several routines manipulate "words"
|
||||
** a "word" is a string not containing whitespace
|
||||
*/
|
||||
|
||||
/*
|
||||
** copy a single word. similar to strcpy
|
||||
*/
|
||||
void
|
||||
S_wordcpy(to,from)
|
||||
char *to, *from;
|
||||
{
|
||||
while ((*from != '\0') && isprint(*from) && (!isspace(*from)))
|
||||
{
|
||||
*to++ = *from++;
|
||||
}
|
||||
*to = '\0';
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
** find the next whitespace character. The address of the pointer
|
||||
** is passed and the pointer itself is changed.
|
||||
*/
|
||||
void
|
||||
S_skipword(theptr)
|
||||
char **theptr;
|
||||
{
|
||||
while((**theptr != '\0') && isprint(**theptr) && (!isspace(**theptr)))
|
||||
{
|
||||
(*theptr)++; /* increment the pointer, NOT the pointer
|
||||
to the pointer */
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
** find the next non-whitespace character. The address of the pointer
|
||||
** is passed and the pointer itself is changed.
|
||||
*/
|
||||
void
|
||||
S_skipspace(theptr)
|
||||
char **theptr;
|
||||
{
|
||||
while((**theptr != '\0') && isspace(**theptr))
|
||||
{
|
||||
(*theptr)++; /* increment the pointer, NOT the pointer
|
||||
to the pointer */
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
** move the pointer to the beginning of the next word
|
||||
*/
|
||||
void
|
||||
S_nextword(theptr)
|
||||
char **theptr;
|
||||
{
|
||||
S_skipword(theptr);
|
||||
S_skipspace(theptr);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
** see if the first string is a prefix of the second
|
||||
** returns 0 if yes
|
||||
** non zero if now
|
||||
** sigh -- the way strcmp does
|
||||
*/
|
||||
int
|
||||
S_wordcmp(s1,s2)
|
||||
char *s1,*s2;
|
||||
{
|
||||
return(strncmp(s1,s2,strlen(s2)));
|
||||
}
|
||||
|
||||
/*
|
||||
** chop off any trailing zeros on a string
|
||||
** but leave one zero if there are only zeros
|
||||
*/
|
||||
void
|
||||
S_trimzeros(str)
|
||||
char *str;
|
||||
{
|
||||
/*
|
||||
** end starts out pointing at the null terminator
|
||||
*/
|
||||
char *end = str + strlen(str);
|
||||
|
||||
/*
|
||||
** if there is more than one character left in the string
|
||||
*/
|
||||
while(end > (str+1))
|
||||
{
|
||||
--end;
|
||||
if ('0' == *end)
|
||||
{
|
||||
*end = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
** save a copy of the string
|
||||
*/
|
||||
void
|
||||
S_savestr(to,from)
|
||||
char **to,*from;
|
||||
{
|
||||
S_allocstr(to,strlen(from));
|
||||
(void) strcpy(*to,from);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
** save cnt characters of the string
|
||||
*/
|
||||
void
|
||||
S_savenstr(to,from,cnt)
|
||||
char **to,*from;
|
||||
{
|
||||
S_allocstr(to,cnt);
|
||||
(void) strncpy(*to,from,cnt);
|
||||
*((*to)+cnt) = '\0';
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
** allocate space for a string, add 1 to size to
|
||||
** make sure that there is room for the terminating null character
|
||||
*/
|
||||
void
|
||||
S_allocstr(to,size)
|
||||
char **to;
|
||||
int size;
|
||||
{
|
||||
*to = Z_ALLOC(size+1,char);
|
||||
}
|
20
utils/Spiff/strings.h
Normal file
20
utils/Spiff/strings.h
Normal file
@ -0,0 +1,20 @@
|
||||
/* Copyright (c) 1988 Bellcore
|
||||
** All Rights Reserved
|
||||
** Permission is granted to copy or use this program, EXCEPT that it
|
||||
** may not be sold for profit, the copyright notice must be reproduced
|
||||
** on copies, and credit should be given to Bellcore where it is due.
|
||||
** BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
|
||||
*/
|
||||
|
||||
#ifndef S_INCLUDED
|
||||
extern void S_wordcpy();
|
||||
extern void S_skipword();
|
||||
extern void S_skipspace();
|
||||
extern void S_nextword();
|
||||
extern int S_wordcmp();
|
||||
extern void S_trimzeros();
|
||||
extern void S_savestr();
|
||||
extern void S_savenstr();
|
||||
extern void S_allocstr();
|
||||
#define S_INCLUDED
|
||||
#endif
|
37
utils/Spiff/token.c
Normal file
37
utils/Spiff/token.c
Normal file
@ -0,0 +1,37 @@
|
||||
/* Copyright (c) 1988 Bellcore
|
||||
** All Rights Reserved
|
||||
** Permission is granted to copy or use this program, EXCEPT that it
|
||||
** may not be sold for profit, the copyright notice must be reproduced
|
||||
** on copies, and credit should be given to Bellcore where it is due.
|
||||
** BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef lint
|
||||
static char rcsid[]= "$Header$";
|
||||
#endif
|
||||
|
||||
#include "misc.h"
|
||||
#include "token.h"
|
||||
|
||||
K_token _K_ato[K_MAXTOKENS]; /* storage for tokens */
|
||||
K_token _K_bto[K_MAXTOKENS];
|
||||
|
||||
int _K_atm;
|
||||
int _K_btm;
|
||||
|
||||
void
|
||||
K_settoken(file,index,pointer)
|
||||
int file;
|
||||
int index;
|
||||
K_token pointer;
|
||||
{
|
||||
if (file)
|
||||
{
|
||||
_K_bto[index] = pointer;
|
||||
}
|
||||
else
|
||||
{
|
||||
_K_ato[index] = pointer;
|
||||
}
|
||||
}
|
81
utils/Spiff/token.h
Normal file
81
utils/Spiff/token.h
Normal file
@ -0,0 +1,81 @@
|
||||
/* Copyright (c) 1988 Bellcore
|
||||
** All Rights Reserved
|
||||
** Permission is granted to copy or use this program, EXCEPT that it
|
||||
** may not be sold for profit, the copyright notice must be reproduced
|
||||
** on copies, and credit should be given to Bellcore where it is due.
|
||||
** BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef K_INCLUDED
|
||||
#include "float.h"
|
||||
#include "tol.h"
|
||||
#include "strings.h"
|
||||
|
||||
#define K_MAXTOKENS 50000
|
||||
/*
|
||||
** values for token type
|
||||
*/
|
||||
#define K_LIT 1
|
||||
#define K_FLO_NUM 2
|
||||
|
||||
|
||||
typedef struct {
|
||||
int linenum; /* line that the token started on */
|
||||
int pos; /* position on the line where token started */
|
||||
int type; /* token type */
|
||||
char *text; /* literal token text */
|
||||
/*
|
||||
** canonical floationg point representation
|
||||
*/
|
||||
F_float flo_num;
|
||||
T_tol tolerance;
|
||||
} _K_str, *K_token;
|
||||
|
||||
/*
|
||||
** this should really be a two dimensional array
|
||||
** but i'm too lazy to recode it
|
||||
*/
|
||||
extern K_token _K_ato[]; /* storage for the tokens */
|
||||
extern K_token _K_bto[];
|
||||
/*
|
||||
** save token X from file
|
||||
*/
|
||||
extern void K_settoken(/*file,X,ptr*/);
|
||||
#define K_gettoken(file, X) (file?(_K_bto[X]):(_K_ato[X]))
|
||||
|
||||
extern int _K_atm; /* count of tokens */
|
||||
extern int _K_btm;
|
||||
|
||||
/*
|
||||
** get token number X from file
|
||||
*/
|
||||
#define K_get_token(file, X) (file?(_K_bto[X]):(_K_ato[X]))
|
||||
|
||||
#define K_gettmax(file) (file?_K_btm:_K_atm)
|
||||
#define K_settmax(file,value) (file?(_K_btm=(value)):(_K_atm=(value)))
|
||||
/*
|
||||
** increment and return true on overflow
|
||||
*/
|
||||
#define K_inctmax(file) ((file?(++_K_btm):(++_K_atm))>=K_MAXTOKENS)
|
||||
|
||||
#define K_setline(x,y) (x->linenum = y)
|
||||
#define K_setpos(x,y) (x->pos = y)
|
||||
#define K_settext(x,y) (x->text = y)
|
||||
#define K_savetext(x,y,z) S_savestr(&(x->text),y)
|
||||
#define K_saventext(x,y,z) S_savenstr(&(x->text),y,z)
|
||||
#define K_setfloat(x,y) (x->flo_num = y)
|
||||
#define K_settol(x,y) (x->tolerance = y)
|
||||
#define K_settype(x,y) (x->type = y)
|
||||
|
||||
#define K_getline(x) (x->linenum)
|
||||
#define K_getpos(x) (x->pos)
|
||||
#define K_gettext(x) (x->text)
|
||||
#define K_getfloat(x) (x->flo_num)
|
||||
#define K_gettol(x) (x->tolerance)
|
||||
#define K_gettype(x) (x->type)
|
||||
|
||||
#define K_maketoken() (Z_ALLOC(1,_K_str))
|
||||
|
||||
#define K_INCLUDED
|
||||
#endif
|
361
utils/Spiff/tol.c
Normal file
361
utils/Spiff/tol.c
Normal file
@ -0,0 +1,361 @@
|
||||
/* Copyright (c) 1988 Bellcore
|
||||
** All Rights Reserved
|
||||
** Permission is granted to copy or use this program, EXCEPT that it
|
||||
** may not be sold for profit, the copyright notice must be reproduced
|
||||
** on copies, and credit should be given to Bellcore where it is due.
|
||||
** BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef lint
|
||||
static char rcsid[]= "$Header$";
|
||||
#endif
|
||||
|
||||
#include "misc.h"
|
||||
#include "float.h"
|
||||
#include "tol.h"
|
||||
#include "token.h"
|
||||
|
||||
/*
|
||||
** storage for the default tolerances
|
||||
*/
|
||||
T_tol _T_gtol = _T_null;
|
||||
|
||||
/*
|
||||
** tolerances that can be set in the command script and attached to floating
|
||||
** point numbers at parse time
|
||||
*/
|
||||
static T_tol _T_tols[_T_TOLMAX];
|
||||
|
||||
/*
|
||||
** initialize the global tolerance
|
||||
** should be called only once at the beginning of the program
|
||||
*/
|
||||
void
|
||||
T_initdefault()
|
||||
{
|
||||
static int called_before = 0;
|
||||
|
||||
if (called_before)
|
||||
{
|
||||
Z_fatal("T_initdefault called more than once\n");
|
||||
}
|
||||
|
||||
/*
|
||||
** if the default tolerance was set somewhere else
|
||||
** don't set it here
|
||||
*/
|
||||
if (T_isnull(_T_gtol))
|
||||
{
|
||||
T_defatol(_T_ADEF);
|
||||
T_defrtol(_T_RDEF);
|
||||
}
|
||||
called_before = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
_T_tolclear(addr)
|
||||
T_tol *addr;
|
||||
{
|
||||
*addr = _T_null;
|
||||
}
|
||||
|
||||
/*
|
||||
** clear the parse time tolerances
|
||||
*/
|
||||
void
|
||||
T_clear_tols()
|
||||
{
|
||||
int i;
|
||||
for(i=0;i<_T_TOLMAX;i++)
|
||||
{
|
||||
_T_tolclear(&_T_tols[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_T_defclear()
|
||||
{
|
||||
_T_tolclear(&_T_gtol);
|
||||
}
|
||||
|
||||
/*
|
||||
** take a series of specifiers and add them to the tolerance
|
||||
*/
|
||||
static void
|
||||
_T_settol(toladdr,str)
|
||||
T_tol *toladdr;
|
||||
char *str;
|
||||
{
|
||||
char typechar;
|
||||
while ('\0' != *str)
|
||||
{
|
||||
/*
|
||||
** find the first non-whitespace character
|
||||
*/
|
||||
S_skipspace(&str);
|
||||
/*
|
||||
** snarf up the type specifier
|
||||
*/
|
||||
typechar = *str;
|
||||
/*
|
||||
** now skip the first char
|
||||
*/
|
||||
str++;
|
||||
/*
|
||||
** skip any possibly intervening whitespace
|
||||
*/
|
||||
S_skipspace(&str);
|
||||
switch (typechar)
|
||||
{
|
||||
case 'a':
|
||||
_T_addtol(toladdr,T_ABSOLUTE,str);
|
||||
break;
|
||||
case 'r':
|
||||
_T_addtol(toladdr,T_RELATIVE,str);
|
||||
break;
|
||||
case 'i':
|
||||
_T_addtol(toladdr,T_IGNORE,(char*)0);
|
||||
break;
|
||||
case 'd':
|
||||
_T_appendtols(toladdr,_T_gtol);
|
||||
break;
|
||||
default:
|
||||
(void) sprintf(Z_err_buf,
|
||||
"don't understand tolerance type '%c'\n",typechar);
|
||||
Z_fatal(Z_err_buf);
|
||||
}
|
||||
/*
|
||||
** and skip to next tolerance
|
||||
*/
|
||||
S_nextword(&str);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** set the default tolerance
|
||||
*/
|
||||
void
|
||||
T_setdef(str)
|
||||
char *str;
|
||||
{
|
||||
_T_defclear();
|
||||
_T_settol(&_T_gtol,str);
|
||||
}
|
||||
|
||||
|
||||
static char*
|
||||
_T_nextspec(ptr)
|
||||
char *ptr;
|
||||
{
|
||||
/*
|
||||
** find the end of the current spec
|
||||
*/
|
||||
for(;(_T_SEPCHAR != *ptr) && ('\0' != *ptr);ptr++)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
** and step over the seperator if necessary
|
||||
*/
|
||||
if (_T_SEPCHAR == *ptr)
|
||||
ptr++;
|
||||
|
||||
return(ptr);
|
||||
}
|
||||
|
||||
/*
|
||||
** return just the next set of specs
|
||||
** ie the string up to end of line or
|
||||
** the first _T_SEPCHAR
|
||||
** returned string does not include the _T_SEPCHAR
|
||||
*/
|
||||
static char *
|
||||
_T_getspec(from)
|
||||
char *from;
|
||||
{
|
||||
static char retval[Z_LINELEN];
|
||||
char *ptr = retval;
|
||||
|
||||
while((_T_SEPCHAR != *from) && ('\0' != *from))
|
||||
{
|
||||
*ptr++ = *from++;
|
||||
}
|
||||
*ptr = '\0'; /* terminate the line */
|
||||
return(retval);
|
||||
}
|
||||
|
||||
/*
|
||||
** parse a series of _T_SEPCHAR separated tolerance specifications
|
||||
*/
|
||||
void
|
||||
T_tolline(str)
|
||||
char *str;
|
||||
{
|
||||
int nexttol;
|
||||
|
||||
T_clear_tols();
|
||||
|
||||
for(nexttol=0;'\0' != *str;nexttol++,str = _T_nextspec(str))
|
||||
{
|
||||
/*
|
||||
** make sure we haven't run off the end
|
||||
*/
|
||||
if (nexttol >= _T_TOLMAX)
|
||||
{
|
||||
Z_fatal("too many tolerances per line");
|
||||
}
|
||||
|
||||
/*
|
||||
** and set the tolerance
|
||||
*/
|
||||
_T_settol(&_T_tols[nexttol],_T_getspec(str));
|
||||
}
|
||||
}
|
||||
|
||||
T_moretols(next_tol)
|
||||
{
|
||||
return((next_tol >= 0) &&
|
||||
(_T_TOLMAX-1 > next_tol) &&
|
||||
(!T_isnull( _T_tols[next_tol+1])));
|
||||
}
|
||||
|
||||
T_tol
|
||||
T_gettol(index)
|
||||
int index;
|
||||
{
|
||||
return(_T_tols[index]);
|
||||
}
|
||||
|
||||
/*
|
||||
** chose which tolerance to use
|
||||
** precidence is
|
||||
** first tolerance
|
||||
** second tolerance
|
||||
** default tolerance
|
||||
*/
|
||||
T_tol
|
||||
T_picktol(p1,p2)
|
||||
T_tol p1, p2;
|
||||
{
|
||||
if (!(T_isnull(p1)))
|
||||
return(p1);
|
||||
|
||||
if (!(T_isnull(p2)))
|
||||
return(p2);
|
||||
|
||||
return(_T_gtol);
|
||||
}
|
||||
|
||||
void
|
||||
_T_appendtols(to,from)
|
||||
T_tol *to,from;
|
||||
{
|
||||
|
||||
T_tol last;
|
||||
|
||||
/*
|
||||
** are there any elements on the list yet
|
||||
*/
|
||||
if (T_isnull(*to))
|
||||
{
|
||||
/*
|
||||
** it's a null list, so allocat space for the
|
||||
** first element and set pointer to it.
|
||||
*/
|
||||
|
||||
*to = from;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
** find the last element on the list
|
||||
*/
|
||||
for(last= *to;!T_isnull(T_getnext(last));last = T_getnext(last))
|
||||
{
|
||||
}
|
||||
/*
|
||||
** add an element on the end
|
||||
*/
|
||||
T_setnext(last,from);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** add a tolerance to a list
|
||||
*/
|
||||
void
|
||||
_T_addtol(listptr,type,str)
|
||||
T_tol *listptr;
|
||||
int type;
|
||||
char *str;
|
||||
{
|
||||
T_tol last;
|
||||
|
||||
/*
|
||||
** are there any elements on the list yet
|
||||
*/
|
||||
if (T_isnull(*listptr))
|
||||
{
|
||||
/*
|
||||
** it's a null list, so allocat space for the
|
||||
** first element and set pointer to it.
|
||||
*/
|
||||
|
||||
last = *listptr = Z_ALLOC(1,_T_struct);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
** find the last element on the list
|
||||
*/
|
||||
for(last= *listptr;!T_isnull(T_getnext(last));last = T_getnext(last))
|
||||
{
|
||||
}
|
||||
/*
|
||||
** add an element on the end
|
||||
*/
|
||||
T_setnext(last,Z_ALLOC(1,_T_struct));
|
||||
|
||||
/*
|
||||
** and point to the new element
|
||||
*/
|
||||
last = T_getnext(last);
|
||||
}
|
||||
|
||||
T_settype(last,type);
|
||||
T_setnext(last,_T_null);
|
||||
|
||||
/*
|
||||
** set the float value only if necessary
|
||||
*/
|
||||
if (T_IGNORE == type)
|
||||
{
|
||||
T_setfloat(last,F_null);
|
||||
}
|
||||
else
|
||||
{
|
||||
T_setfloat(last,F_atof(str,NO_USE_ALL));
|
||||
|
||||
/*
|
||||
** test new tolerance for sanity
|
||||
*/
|
||||
if (F_getsign(T_getfloat(last)))
|
||||
{
|
||||
(void) sprintf(Z_err_buf,
|
||||
"%s : negative tolerances don't make any sense\n",str);
|
||||
Z_fatal(Z_err_buf);
|
||||
}
|
||||
/*
|
||||
** check for excessively large relative tolerances
|
||||
*/
|
||||
if ((T_RELATIVE == type) &&
|
||||
(F_floatcmp(T_getfloat(last),
|
||||
F_atof("2.0",USE_ALL)) > 0))
|
||||
{
|
||||
(void) sprintf(Z_err_buf,
|
||||
"%s : relative tolerances greater than 2 don't make any sense\n",str);
|
||||
Z_fatal(Z_err_buf);
|
||||
}
|
||||
}
|
||||
}
|
64
utils/Spiff/tol.h
Normal file
64
utils/Spiff/tol.h
Normal file
@ -0,0 +1,64 @@
|
||||
/* Copyright (c) 1988 Bellcore
|
||||
** All Rights Reserved
|
||||
** Permission is granted to copy or use this program, EXCEPT that it
|
||||
** may not be sold for profit, the copyright notice must be reproduced
|
||||
** on copies, and credit should be given to Bellcore where it is due.
|
||||
** BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
|
||||
*/
|
||||
|
||||
#include "float.h"
|
||||
|
||||
#ifndef T_INCLUDED
|
||||
/*
|
||||
** values for tol_type
|
||||
*/
|
||||
#define T_ABSOLUTE 0
|
||||
#define T_RELATIVE 1
|
||||
#define T_IGNORE 2
|
||||
|
||||
typedef struct _T_tstr{
|
||||
int tol_type; /* one of the above */
|
||||
F_float flo_tol; /* tolerance is expressed in
|
||||
terms of a floating point value */
|
||||
struct _T_tstr *next;
|
||||
} _T_struct, *T_tol;
|
||||
|
||||
#define _T_TOLMAX 10 /* number of tolerances that can
|
||||
be in effect at one time */
|
||||
|
||||
#define _T_ADEF "1e-10" /* default absolute tolerance */
|
||||
#define _T_RDEF "1e-10" /* default relative tolerance */
|
||||
|
||||
extern T_tol T_gettol();
|
||||
extern void T_clear_tols();
|
||||
extern void T_initdefault();
|
||||
extern void T_setdef();
|
||||
extern void T_tolline();
|
||||
extern T_tol T_picktol();
|
||||
|
||||
#define T_gettype(x) (x->tol_type)
|
||||
#define T_getfloat(x) (x->flo_tol)
|
||||
#define T_getnext(x) (x->next)
|
||||
|
||||
#define T_settype(x,y) (x->tol_type = y)
|
||||
#define T_setfloat(x,y) (x->flo_tol = y)
|
||||
#define T_setnext(x,y) (x->next = y)
|
||||
|
||||
#define _T_null ((T_tol) 0)
|
||||
#define T_isnull(x) ((x) == _T_null)
|
||||
|
||||
extern T_tol _T_gtol;
|
||||
extern void _T_addtol();
|
||||
extern void _T_appendtols();
|
||||
|
||||
/*
|
||||
** routines for building the global tolerance list
|
||||
*/
|
||||
#define T_defatol(x) _T_addtol(&_T_gtol,T_ABSOLUTE,x)
|
||||
#define T_defrtol(x) _T_addtol(&_T_gtol,T_RELATIVE,x)
|
||||
#define T_defitol() _T_addtol(&_T_gtol,T_IGNORE,(char*)NULL)
|
||||
|
||||
#define _T_SEPCHAR ';'
|
||||
|
||||
#define T_INCLUDED
|
||||
#endif
|
411
utils/Spiff/visual.c
Normal file
411
utils/Spiff/visual.c
Normal file
@ -0,0 +1,411 @@
|
||||
/* Copyright (c) 1988 Bellcore
|
||||
** All Rights Reserved
|
||||
** Permission is granted to copy or use this program, EXCEPT that it
|
||||
** may not be sold for profit, the copyright notice must be reproduced
|
||||
** on copies, and credit should be given to Bellcore where it is due.
|
||||
** BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef lint
|
||||
static char rcsid[]= "$Header$";
|
||||
#endif
|
||||
|
||||
#ifdef MGR
|
||||
|
||||
#include "misc.h"
|
||||
#include "line.h"
|
||||
#include "token.h"
|
||||
#include "/usr/public/pkg/mgr/include/term.h"
|
||||
#include "/usr/public/pkg/mgr/include/restart.h"
|
||||
|
||||
#define OTHER 0
|
||||
#define ON_DEBUG 1
|
||||
#define OFF_DEBUG 2
|
||||
#define DO_QUIT 3
|
||||
#define DO_PAGE 4
|
||||
#define NEW_PREC 5
|
||||
|
||||
|
||||
#define NROW 60
|
||||
#define NCOL 80
|
||||
|
||||
int isdiff[MAXTOKENS]; /* flag showing if a token pair was shown different*/
|
||||
|
||||
int comwin,wina, winb; /* location to store window numbers */
|
||||
int fontx,fonty; /* size of the font in pixels */
|
||||
|
||||
int debug =0;
|
||||
|
||||
|
||||
int firsttoken = 0; /* index of first token pair being displayed */
|
||||
int tokencnt; /* count of the number of token pairs being displayed */
|
||||
|
||||
V_visual(flags)
|
||||
int flags;
|
||||
{
|
||||
|
||||
int moretodo = 1; /* flag to clear when we're finished */
|
||||
|
||||
messup();
|
||||
|
||||
m_selectwin(comwin);
|
||||
m_setmode(W_ACTIVATE);
|
||||
|
||||
showpages(comroutine,flags);
|
||||
|
||||
do
|
||||
{
|
||||
switch(getinput())
|
||||
{
|
||||
case ON_DEBUG:
|
||||
debug = 0;
|
||||
break;
|
||||
case OFF_DEBUG:
|
||||
debug = 1;
|
||||
break;
|
||||
case DO_QUIT:
|
||||
moretodo = 0;
|
||||
break;
|
||||
case DO_PAGE:
|
||||
if((firsttoken+tokencnt>= K_gettmax(0))||
|
||||
(firsttoken+tokencnt>= K_gettmax(1)))
|
||||
{
|
||||
m_selectwin(comwin);
|
||||
m_printstr("\007this is the last page\n");
|
||||
break;
|
||||
}
|
||||
firsttoken += tokencnt;
|
||||
showpages(comroutine,flags);
|
||||
break;
|
||||
case NEW_PREC:
|
||||
updatepages(comroutine,flags);
|
||||
break;
|
||||
case OTHER:
|
||||
break;
|
||||
default :
|
||||
Z_fatal("bad value in main switch");
|
||||
|
||||
}
|
||||
} while (moretodo);
|
||||
|
||||
V_cleanup();
|
||||
return(0);
|
||||
}
|
||||
|
||||
getinput()
|
||||
{
|
||||
char ibuf[Z_LINELEN]; /* input buffer */
|
||||
char *ptr;
|
||||
|
||||
m_selectwin(comwin);
|
||||
m_setmode(W_ACTIVATE);
|
||||
switch (m_getchar())
|
||||
{
|
||||
case 't':
|
||||
m_gets(ibuf);
|
||||
/*
|
||||
** skip the 'tol'
|
||||
*/
|
||||
ptr = ibuf;
|
||||
S_nextword(&ptr);
|
||||
T_setdef(ptr);
|
||||
return(NEW_PREC);
|
||||
case 'q':
|
||||
return(DO_QUIT);
|
||||
case 'd':
|
||||
return(OFF_DEBUG);
|
||||
case 'D':
|
||||
return(ON_DEBUG);
|
||||
case 'm':
|
||||
return(DO_PAGE);
|
||||
default:
|
||||
return(OTHER);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
showpages(comroutine,flags)
|
||||
int (*comroutine)();
|
||||
int flags;
|
||||
{
|
||||
int i;
|
||||
m_selectwin(wina);
|
||||
m_clear();
|
||||
m_selectwin(winb);
|
||||
m_clear();
|
||||
showlines();
|
||||
|
||||
for(i=firsttoken;i<tokencnt+firsttoken; i++)
|
||||
{
|
||||
isdiff[i] = 0;
|
||||
}
|
||||
updatepages(comroutine,flags);
|
||||
}
|
||||
|
||||
updatepages(comroutine,flags)
|
||||
int (*comroutine)();
|
||||
int flags;
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=firsttoken;i<tokencnt+firsttoken; i++)
|
||||
{
|
||||
if (isdiff[i])
|
||||
{
|
||||
|
||||
if (0 == X_com(i,i,flags))
|
||||
{
|
||||
m_selectwin(wina);
|
||||
un_highlight(0,K_gettoken(0,i),K_getline(K_gettoken(0,firsttoken)));
|
||||
m_selectwin(winb);
|
||||
un_highlight(1,K_gettoken(1,i),K_getline(K_gettoken(1,firsttoken)));
|
||||
isdiff[i] = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (0 != X_com(i,i,flags))
|
||||
{
|
||||
m_selectwin(wina);
|
||||
highlight(0,K_gettoken(0,i),K_getline(K_gettoken(0,firsttoken)));
|
||||
m_selectwin(winb);
|
||||
highlight(1,K_gettoken(1,i),K_getline(K_gettoken(1,firsttoken)));
|
||||
isdiff[i] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
un_highlight(file,ptr,firstline)
|
||||
int file;
|
||||
K_token ptr;
|
||||
int firstline;
|
||||
{
|
||||
highlight(file,ptr,firstline);
|
||||
}
|
||||
|
||||
/*
|
||||
** argument expressed in terms of token lines
|
||||
*/
|
||||
highlight(file,ptr,firstline)
|
||||
int file;
|
||||
K_token ptr;
|
||||
int firstline;
|
||||
{
|
||||
int startx = K_getpos(ptr)*fontx;
|
||||
int starty = (L_tl2cl(file,K_getline(ptr))-L_tl2cl(file,firstline))*fonty;
|
||||
|
||||
int sizex = fontx*strlen(K_gettext(ptr));
|
||||
int sizey = fonty;
|
||||
m_bitwrite(startx,starty,sizex,sizey);
|
||||
}
|
||||
|
||||
showlines()
|
||||
{
|
||||
int Alinecnt = 0;
|
||||
int Blinecnt = 0;
|
||||
|
||||
int Atfirstline = K_getline(K_gettoken(0,firsttoken));
|
||||
int Btfirstline = K_getline(K_gettoken(1,firsttoken));
|
||||
int Afirstline = L_tl2cl(0,K_getline(K_gettoken(0,firsttoken)));
|
||||
int Bfirstline = L_tl2cl(1,K_getline(K_gettoken(1,firsttoken)));
|
||||
int Anexttoken = L_getindex(0,Atfirstline);
|
||||
int Bnexttoken = L_getindex(1,Btfirstline);
|
||||
int i;
|
||||
/*
|
||||
** first print the lines on the screen
|
||||
*/
|
||||
for(i=0;i < NROW;i++)
|
||||
{
|
||||
if(Afirstline+i < L_getclmax(0))
|
||||
{
|
||||
m_selectwin(wina);
|
||||
showline(0,Afirstline+i,i);
|
||||
Alinecnt++;
|
||||
}
|
||||
|
||||
if(Bfirstline+i < L_getclmax(1))
|
||||
{
|
||||
m_selectwin(winb);
|
||||
showline(1,Bfirstline+i,i);
|
||||
Blinecnt++;
|
||||
}
|
||||
}
|
||||
/*
|
||||
** now figure out how many tokens we actually printed
|
||||
*/
|
||||
for(i=Atfirstline;Anexttoken<K_gettmax(0) && L_tl2cl(0,i) < Afirstline+Alinecnt;i++)
|
||||
{
|
||||
Anexttoken += L_getcount(0,i);
|
||||
}
|
||||
|
||||
for(i=Btfirstline;Bnexttoken<K_gettmax(1) && L_tl2cl(1,i) < Bfirstline+Blinecnt;i++)
|
||||
{
|
||||
Bnexttoken += L_getcount(1,i);
|
||||
}
|
||||
tokencnt = MIN(Anexttoken,Bnexttoken) - firsttoken;
|
||||
|
||||
/*
|
||||
** draw a line through any tokens that come before the first
|
||||
** token that is being compared
|
||||
*/
|
||||
if (L_getindex(0,Atfirstline) != firsttoken)
|
||||
{
|
||||
m_selectwin(wina);
|
||||
for(i=L_getindex(0,Atfirstline);i<firsttoken;i++)
|
||||
{
|
||||
drawline(K_gettoken(0,i),0);
|
||||
}
|
||||
}
|
||||
|
||||
if (L_getindex(1,Btfirstline) != firsttoken)
|
||||
{
|
||||
m_selectwin(winb);
|
||||
|
||||
for(i=L_getindex(1,Btfirstline);i<firsttoken;i++)
|
||||
{
|
||||
drawline(K_gettoken(1,i),0);
|
||||
}
|
||||
/*
|
||||
m_line(Bt[Bindex[Bfirstline]]->pos*fontx,fonty/2,(Bt[firsttoken]->pos*fontx)-2,fonty/2);
|
||||
*/
|
||||
}
|
||||
|
||||
if (Anexttoken > Bnexttoken)
|
||||
{
|
||||
m_selectwin(wina);
|
||||
for(i=Bnexttoken;i<Anexttoken;i++)
|
||||
{
|
||||
drawline(K_gettoken(0,i),L_tl2cl(0,K_getline(K_gettoken(0,i)))-Afirstline);
|
||||
}
|
||||
}
|
||||
|
||||
if (Anexttoken < Bnexttoken)
|
||||
{
|
||||
m_selectwin(winb);
|
||||
for(i=Anexttoken;i<Bnexttoken;i++)
|
||||
{
|
||||
drawline(K_gettoken(1,i),L_tl2cl(1,K_getline(K_gettoken(1,i)))-Bfirstline);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
** line is given in conten line
|
||||
*/
|
||||
drawline(ptr,line)
|
||||
K_token ptr;
|
||||
int line;
|
||||
{
|
||||
m_line(K_getpos(ptr)*fontx,
|
||||
(line*fonty)+fonty/2,
|
||||
(K_getpos(ptr)+strlen(K_gettext(ptr)))*fontx,
|
||||
(line*fonty)+fonty/2);
|
||||
}
|
||||
|
||||
/*
|
||||
** takes arguments in terms of content lines
|
||||
*/
|
||||
showline(file,index,row)
|
||||
int file;
|
||||
int index;
|
||||
int row;
|
||||
{
|
||||
static char tmp[Z_LINELEN];
|
||||
m_move(0,row);
|
||||
stripnl(tmp,L_getcline(file,index));
|
||||
m_printstr(tmp);
|
||||
}
|
||||
|
||||
stripnl(to,from)
|
||||
char *to,*from;
|
||||
{
|
||||
while ((*from != '\n') && (*from != '\0'))
|
||||
{
|
||||
*to++ = *from++;
|
||||
}
|
||||
*to = '\0';
|
||||
}
|
||||
|
||||
static int didscr = 0;
|
||||
|
||||
messup()
|
||||
{
|
||||
int col, row;
|
||||
int dum1,dum2,dum3,border;
|
||||
|
||||
m_setup(W_FLUSH|W_DEBUG);
|
||||
m_push(P_EVENT|P_FLAGS|P_POSITION);
|
||||
get_param(&dum1,&dum2,&dum3,&border);
|
||||
didscr = 1;
|
||||
comwin = m_makewindow(192,50,732,116);
|
||||
wina = m_makewindow(0,218,570,670);
|
||||
m_selectwin(wina);
|
||||
m_font(2);
|
||||
get_font(&fontx,&fonty);
|
||||
m_shapewindow(0,218,NCOL*fontx+(2*border),NROW*fonty+(2*border));
|
||||
|
||||
get_colrow(&col,&row);
|
||||
if ((col != NCOL) || (row != NROW))
|
||||
{
|
||||
Z_fatal("bad window size");
|
||||
}
|
||||
m_func(B_INVERT);
|
||||
m_setmode(W_ABS);
|
||||
|
||||
winb = m_makewindow(580,218,570,670);
|
||||
m_selectwin(winb);
|
||||
m_font(2);
|
||||
get_font(&fontx,&fonty);
|
||||
m_shapewindow(580,218,NCOL*fontx+(2*border),NROW*fonty+(2*border));
|
||||
|
||||
get_colrow(&col,&row);
|
||||
if ((col != NCOL) || (row != NROW))
|
||||
{
|
||||
Z_fatal("bad window size");
|
||||
}
|
||||
m_func(B_INVERT);
|
||||
m_setmode(W_ABS);
|
||||
|
||||
m_selectwin(comwin);
|
||||
m_clear();
|
||||
m_setmode(W_ABS);
|
||||
m_setmode(W_ACTIVATE);
|
||||
}
|
||||
|
||||
V_cleanup()
|
||||
{
|
||||
if (didscr)
|
||||
{
|
||||
m_destroywin(wina);
|
||||
m_destroywin(winb);
|
||||
m_destroywin(comwin);
|
||||
m_popall();
|
||||
m_setecho();
|
||||
(void) fclose(m_termin);
|
||||
(void) fclose(m_termout);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include "misc.h"
|
||||
/*
|
||||
** dummy code for systems that don't have
|
||||
** the mgr window manager installed
|
||||
*/
|
||||
V_visual(d)
|
||||
int d;
|
||||
{
|
||||
Z_fatal("visual mode is not available on this machine\n");
|
||||
return(-d); /* boiler plate */
|
||||
}
|
||||
|
||||
void
|
||||
V_cleanup()
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
17
utils/Spiff/visual.h
Normal file
17
utils/Spiff/visual.h
Normal file
@ -0,0 +1,17 @@
|
||||
/* Copyright (c) 1988 Bellcore
|
||||
** All Rights Reserved
|
||||
** Permission is granted to copy or use this program, EXCEPT that it
|
||||
** may not be sold for profit, the copyright notice must be reproduced
|
||||
** on copies, and credit should be given to Bellcore where it is due.
|
||||
** BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef V_INCLUDED
|
||||
|
||||
|
||||
extern void V_cleanup();
|
||||
|
||||
#define V_INCLUDED
|
||||
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user