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