home *** CD-ROM | disk | FTP | other *** search
- /*------------------------------------------------------------------------------
- --------------------------------------------------------------------------------
- --
- -- Name: DIFF.C
- -- Processor: VAX | MS-DOS
- -- Class: C Program
- -- Creation Date: 1/8/87
- -- Revision: 07/23/87 by J.K.LaPeer for Megamax C compiler for Atari ST
- -- Author: D. Krantz
- --
- -- Description: File compare and change-bar for text files.
- --
- --------------------------------------------------------------------------------
- ------------------------------------------------------------------------------*/
-
- /* File Difference Utility */
-
- #include <ctype.h>
- #include <stdio.h>
-
- #define OPT_FLAG '/' /* command line option switch recognizer */
-
- #ifdef VAX11C
- #define MAXLINE 161 /* maximum characters in input line */
- #else
- #define MAXLINE 85
- #endif
-
- #define FORMFEED 'L'-'@'
-
- struct LINE { /* structure defining a line internally */
- int linenum; /* what line on page */
- int pagenum; /* what page line is from */
- struct LINE *link; /* linked list pointer */
- char text[ MAXLINE ]; /* text of line */
- char dup[ MAXLINE ]; /* uppercase copy of line text */
- };
-
- typedef struct LINE *line_ptr;
-
- typedef char *char_ptr;
-
- typedef FILE *FILE_PTR;
-
- struct LINE root[ 3 ]; /* root of internal linked lists */
-
- FILE_PTR msg; /* differences summary file pointer */
-
- int line_count[ 3 ] = { 1, 1, 1 }; /* file's line counter */
- int page_count[ 3 ] = { 1, 1, 1 }; /* file's page counter */
- int command_errors = 0; /* how many command line errors */
- char xx1[ 132 ], xx2[ 132 ]; /* space to retain file names */
- int files = 0; /* how many files specified on command line */
- char_ptr infile_name[ 3 ] = { NULL, xx1, xx2 };
- char outfile_name[ 132 ]; /* changebarred output filename */
- FILE_PTR infile[ 3 ]; /* input file pointers */
- FILE *outfile; /* changebarred output file pointer */
- static line_ptr at[ 3 ] = { NULL, &(root[ 1 ]), &(root[ 2 ]) };
-
- int debug = 0; /* trace switch */
- int trace_enabled = 0; /* keyboard tracing switch */
- int bar_col = 78; /* column where change bar is to appear */
- int top_skip = 0; /* lines to skip at top of page */
- int bot_skip = 0; /* lines to skip at bottom of page */
- int page_len = 66; /* length of a page */
- int up_case = 0; /* boolean, is upper/lower case significant? */
- int re_sync = 5; /* lines that must match for resynchronization */
- int output = 0; /* boolean, is change-barred output file on? */
- int blanks = 0; /* boolean, are blank lines significant? */
- int lookahead = 200; /* how many lines to look ahead before giving up*/
- int skip1 = 0; /* how many pages of first file to skip */
- int skip2 = 0; /* how many pages of second file to skip */
-
- #if 0 /* tracing and other debug functions turned off */
-
- #define trace( x ) callstack( x )
- #define ret { callpop(); return; }
- #define ret_val( x ) { callpop(); return( x ); }
- #define TRACER_FUNCTIONS
-
- #else
-
- #define trace( x ) /** nothing **/
- #define ret { return; }
- #define ret_val( x ) { return( x ); }
-
- #endif
-
- /*------------------------------------------------------------------------------
- ------------------------------------------------------------------------------*/
- main( argc, argv )
- int argc;
- char *argv[];
- {
- int i;
- trace( "main" );
- if( argc == 1 )
- help();
- msg = stdout;
- for( i = 1; i < argc; i++ )
- strip_opt( argv[ i ] );
- if( files < 2 )
- {
- printf( "\nError: Must specify two files" );
- exit( 2 );
- }
- open_files();
- if( command_errors )
- exit( 2 );
- page_skip();
- diff();
- ret;
- }
-
- /*------------------------------------------------------------------------------
- DONT_LOOK - Tells us whether or not this line should be considered for
- comparison or is a filler (e.g. header, blank) line.
- ------------------------------------------------------------------------------*/
- dont_look( line )
- line_ptr line;
- {
- int i;
- trace( "dont_look" );
- if( line == NULL )
- ret_val( 0 );
- if( line->linenum <= top_skip )
- ret_val( 1 );
- if( line->linenum > page_len - bot_skip )
- ret_val( 1 );
- if( !blanks )
- {
- for( i = 0; i < MAXLINE; i++ )
- switch( line->text[ i ] )
- {
- case '\0':
- case '\n':
- ret_val( 1 );
- case '\t':
- case ' ':
- break;
- default:
- ret_val( 0 );
- }
- }
- ret_val( 0 );
- }
-
- /*------------------------------------------------------------------------------
- EQUAL - tells us if the pointers 'a' and 'b' point to line buffers containing
- equivalent text or not.
- ------------------------------------------------------------------------------*/
- equal( a, b )
- line_ptr a, b;
- {
- trace( "equal" );
- if( (a == NULL) || (b == NULL) )
- ret_val( 0 );
- if( up_case )
- ret_val( !strcmp( a->dup, b->dup ) )
- else
- ret_val( !strcmp( a->text, b->text ) )
- }
-
- /*------------------------------------------------------------------------------
- POSITION - moves the input pointer for file 'f' such that the next line to
- be read will be 'where'.
- ------------------------------------------------------------------------------*/
- position( f, where )
- int f;
- line_ptr where;
- {
- trace( "position" );
- at[ f ] = &root[ f ];
- while( at[ f ]->link != where )
- at[ f ] = at[ f ]->link;
- ret;
- }
-
- /*------------------------------------------------------------------------------
- FIX - fixes the end-of-line sequence on a VAX to be just a newline instead of
- a carriage-return/newline.
- ------------------------------------------------------------------------------*/
- char *fix( str )
- char *str;
- {
- char *strsave;
-
- trace( "fix" );
- strsave = str;
- if( str == NULL )
- ret_val( NULL )
- #ifdef VAX11C
- while( *str != '\0' )
- {
- if( match( str, "\r\n" ) )
- {
- *str = '\n';
- *(str + 1) = '\0';
- }
- str++;
- }
- #endif
- ret_val( strsave );
- }
-
- /*------------------------------------------------------------------------------
- INDEX - returns a pointer to the first occurance of 'c' in the string pointed
- to by 'str', or NULL if 'str' does not contain 'c'.
- ------------------------------------------------------------------------------*/
- char *index( str, c )
- char *str, c;
- {
- trace( "index" );
- while( (*str != c) && *(str++) );
- if( *str == c )
- ret_val( str )
- ret_val( NULL );
- }
-
- /*------------------------------------------------------------------------------
- NEXT_LINE - allocates, links, and returns the next line from file 'f' if no
- lines are buffered, otherwise returns the next buffered line from file 'f'
- and updates the link pointer to the next buffered line.
- ------------------------------------------------------------------------------*/
- line_ptr next_line( f )
- int f;
- {
- char *malloc();
- line_ptr temp, place_hold;
-
- trace( "next_line" );
- if( at[ f ]->link != NULL )
- {
- at[ f ] = at[ f ]->link;
- ret_val( at[ f ] );
- }
- else
- {
- at[ f ]->link = (line_ptr)malloc( sizeof( struct LINE ) );
- if( at[ f ]->link == NULL )
- {
- printf( "\nOut of Memory" );
- exit( 2 );
- }
- place_hold = at[ f ];
- at[ f ] = at[ f ]->link;
- at[ f ]->link = NULL;
- if( fix( fgets( at[ f ]->text, MAXLINE, infile[ f ] ) ) == NULL)
- {
- free( at[ f ] );
- at[ f ] = place_hold;
- at[ f ]->link = NULL;
- ret_val( NULL )
- }
- #ifdef EMBEDDED_FORMFEEDS
- if( (index( at[ f ]->text, FORMFEED ) != NULL) ||
- (line_count[ f ] > page_len ) )
- #else
- if( ( *(at[ f ]->text) == FORMFEED) ||
- (line_count[ f ] > page_len ) )
- #endif
- {
- page_count[ f ]++;
- line_count[ f ] = 1;
- }
- at[ f ]->linenum = line_count[ f ]++;
- at[ f ]->pagenum = page_count[ f ];
- if( up_case )
- {
- strcpy( at[ f ]->dup, at[ f ]->text );
- upper( at[ f ]->dup );
- }
- ret_val( at[ f ] );
- }
- }
-
- /*------------------------------------------------------------------------------
- DISCARD - deallocates all buffered lines from the root up to and including
- 'to' for file 'f'.
- ------------------------------------------------------------------------------*/
- discard( f, to )
- int f;
- line_ptr to;
- {
- line_ptr temp;
-
- trace( "discard" );
- for(;;)
- {
- if( root[ f ].link == NULL )
- break;
- temp = root[ f ].link;
- root[ f ].link = root[ f ].link->link;
- free( temp );
- if( temp == to )
- break;
- }
- at[ f ] = &root[ f ];
- ret;
- }
-
- /*------------------------------------------------------------------------------
- VFPUTS - for VAX, un-fixes newline at end of line to be carriage-return/newline.
- ------------------------------------------------------------------------------*/
- vfputs( str, file )
- char *str;
- FILE *file;
- {
- int i;
- trace( "vfputs" );
- #ifdef VAX11C
- for( i = 0; i < MAXLINE; i++ )
- {
- if( str[ i ] == '\n' )
- {
- strcpy( str + i, "\r\n" );
- break;
- }
- }
- fputs( str, file );
- #else
- fputs( str, file );
- #endif
- ret;
- }
-
- /*------------------------------------------------------------------------------
- PUT - If change-barred output file is turned on, prints all lines from the
- root of file 1 up to and including 'line'. This is called only if a match
- exists for each significant line in file 2.
- ------------------------------------------------------------------------------*/
- put( line )
- line_ptr line;
- {
- line_ptr temp;
-
- trace( "put" );
- if( output )
- for( temp = root[ 1 ].link; ; )
- {
- if( temp == NULL )
- ret
- vfputs( temp->text, outfile );
- if( temp == line )
- ret
- temp = temp->link;
- }
- ret;
- }
-
- /*------------------------------------------------------------------------------
- CHANGE_BAR - inserts a change-bar into the text pointed to by
- 'str' and returns a pointer to 'str'.
- ------------------------------------------------------------------------------*/
- char *change_bar( str )
- char *str;
- {
- int i;
- char temp[ MAXLINE + 1 ], *dest,*base;
-
- trace( "change_bar" );
- base = str;
- dest = temp;
- i = 0;
- if( bar_col != 0 )
- {
- for( i = 0; *str != '\n'; i++ )
- {
- if( (*str == '\r') && (*(str + 1) != '\n') )
- i = 0;
- *(dest++) = *(str++);
- }
- while( i++ < bar_col )
- *(str)++ = ' ';
- strcpy( str, "|\n" );
- }
- else
- if( str[ 0 ] != ' ' )
- {
- strcpy( temp, str );
- strcpy( str + 1, temp );
- str[ 0 ] = '|';
- }
- ret_val( base );
- }
-
- /*------------------------------------------------------------------------------
- ADDED - Prints a change summary for all significant lines from the root of
- file 1 up to and including 'line'. If output is enabled, adds a change bar
- to the text and outputs the line to the output file.
- ------------------------------------------------------------------------------*/
- added( line )
- line_ptr line;
- {
- line_ptr temp;
-
- trace( "added" );
- for( temp = root[ 1 ].link; ; )
- {
- if( temp == NULL )
- ret
- if( !dont_look( temp ) )
- fprintf( msg, "+%d:%d -> %s", temp->pagenum,
- temp->linenum, temp->text );
- if( output )
- if( dont_look( temp ) )
- vfputs( temp->text, outfile );
- else
- vfputs( change_bar( temp->text ), outfile );
- if( temp == line )
- ret
- temp = temp->link;
- }
- }
-
- /*------------------------------------------------------------------------------
- DELETED - outputs a change summary for all lines in file 2 from the root up to
- and including 'line'.
- ------------------------------------------------------------------------------*/
- deleted( line )
- line_ptr line;
- {
- line_ptr temp;
-
- trace( "deleted" );
- for( temp = root[ 2 ].link; ; )
- {
- if( temp == NULL )
- ret
- if( !dont_look( temp ) )
- fprintf( msg, "-%d:%d -> %s", temp->pagenum,
- temp->linenum, temp->text );
- if( temp == line )
- ret
- temp = temp->link;
- }
- ret;
- }
-
- /*------------------------------------------------------------------------------
- RESYNC - resynchronizes file 1 and file 2 after a difference is detected, and
- outputs changed lines and change summaries via added() and deleted(). Exits
- with the file inputs pointing at the next two lines that match, unless
- it is impossible to sync up again, in which case all lines in file 1 are
- printed via added(). Deallocates all lines printed by this function.
- ------------------------------------------------------------------------------*/
- resync( first, second )
- line_ptr first, second;
- {
- line_ptr file1_start, file2_start, last_bad1, last_bad2, t1, t2;
- int i, j ,k, moved1, moved2;
-
- trace( "resync" );
-
- moved1 = 0;
- file1_start = first;
-
- position( 1, first );
- for( k = 0; k < lookahead; k++ )
- {
- while( dont_look( file1_start = next_line( 1 ) ) );
- if( file1_start == NULL ) goto no_sy;
-
- moved2 = 0;
- file2_start = second;
-
- position( 2, second );
- for( j = 0; j < lookahead ; j++ )
- {
- while( dont_look( file2_start = next_line( 2 ) ) );
- if( file2_start == NULL ) goto eof2;
-
- t1 = file1_start;
- t2 = file2_start;
- for( i = 0; (i < re_sync) && equal( t1, t2 ); i++ )
- {
- while( dont_look( t1 = next_line( 1 ) ) );
- while( dont_look( t2 = next_line( 2 ) ) );
- if( (t1 == NULL) || (t2 == NULL) )
- break;
- }
- if( i == re_sync ) goto synced;
-
- last_bad2 = file2_start;
- position( 2, file2_start );
- while( dont_look( file2_start = next_line( 2 ) ) );
- moved2 ++;
- }
- eof2:
- last_bad1 = file1_start;
- position( 1, file1_start );
- while( dont_look( file1_start = next_line( 1 ) ) );
- moved1++;
- }
- printf( "\n*** ERROR - lost sync in file %s at page %d line %d",
- infile_name[ 1 ], first->pagenum, first->linenum );
- fclose( outfile );
- exit( 2 );
- no_sy:
- position( 1, first );
- while( (first = next_line( 1 )) != NULL )
- {
- added( first );
- discard( 1, first );
- }
- ret;
- synced:
- if( moved1 )
- {
- added( last_bad1 );
- discard( 1, last_bad1 );
- }
- position( 1, file1_start );
- if( moved2 )
- {
- deleted( last_bad2 );
- discard( 2, last_bad2 );
- }
- position( 2, file2_start );
- fprintf( msg, "\n" );
- ret;
- }
-
- /*------------------------------------------------------------------------------
- DIFF - differencing executive. Prints and deallocates all lines up to where
- a difference is detected, at which point resync() is called. Exits on end
- of file 1.
- ------------------------------------------------------------------------------*/
- diff()
- {
- line_ptr first, second;
-
- trace( "diff" );
- for( ;; )
- {
- while( dont_look( first = next_line( 1 ) ) );
- if( first == NULL )
- {
- put( first );
- ret;
- }
- while( dont_look( second = next_line( 2 ) ) );
- if( equal( first, second ) )
- {
- put( first );
- discard( 1, first );
- discard( 2, second );
- }
- else
- resync( first, second );
- if( second == NULL )
- ret
- }
- }
-
- /*------------------------------------------------------------------------------
- PAGE_SKIP - skips the first 'skip1' pages of file 1, and then the first 'skip2'
- pages of file 2. This is useful to jump over tables of contents, etc.
- ------------------------------------------------------------------------------*/
- page_skip()
- {
- line_ptr first, second;
-
- trace( "page_skip" );
- for( ; ; )
- {
- first = next_line( 1 );
- if( (first == NULL) || (first->pagenum > skip1) )
- break;
- put( first );
- discard( 1, first );
- }
- if( first != NULL )
- position( 1, first );
- for( ; ; )
- {
- second = next_line( 2 );
- if( (second == NULL) || (second->pagenum > skip2) )
- break;
- discard( 2, second );
- }
- if( second != NULL )
- position( 2, second );
- ret;
- }
-
- /*------------------------------------------------------------------------------
- HELP - outputs usage information.
- ------------------------------------------------------------------------------*/
- help()
- {
- printf( "\nDIFF" );
- printf( "\nText File Differencer and Change Barrer" );
- printf( "\n" );
- printf( "\nFormat:" );
- printf( "\nDIFF [option{option}] newfile oldfile [barfile]" );
- printf( "\n" );
- printf( "\n newfile = latest revision of text file" );
- printf( "\n oldfile = baseline to compare against" );
- printf( "\n barfile = output file if changebars are desired" );
- printf( "\n" );
- printf( "\nOptions:" );
- #ifdef TRACER_FUNCTIONS
- printf( "\n /TRACE Makes a mess of the display and runs real s\
- low" );
- printf( "\n default = trace off" );
- printf( "\n" );
- #endif
- printf( "\n /BAR_COL=n Column of output file in which change bar w\
- ill appear" );
- printf( "\n default = 78" );
- printf( "\n" );
- printf( "\n /TOP_SKIP=n Lines at top of page to skip for running he\
- ads & page nos." );
- printf( "\n default = 0" );
- printf( "\n" );
- printf( "\n /BOT_SKIP=n Lines at botom of page to skip for running \
- foots and page nos." );
- printf( "\n default = 0" );
- printf( "\n" );
- printf( "\n /PAGE_LEN=n Lines per page (embedded formfeeds overrrid\
- e)" );
- printf( "\n default = 66" );
- printf( "\n" );
- printf( "\n /UP_CASE Upper/Lower case is significant/is not sign\
- ificant" );
- printf( "\n /NOUP_CASE default" );
- printf( "\n" );
- printf( "\n /RE_SYNC=n Lines that must match before files are cons\
- idered synced" );
- printf( "\n after differences are found - default = 5" );
- printf( "\n" );
- printf( "\n /OUTPUT=file File to redirect differences summary to. " );
- printf( "\n default = SYS$OUTPUT or console." );
- printf( "\n" );
- printf( "\n /BLANKS Blank lines are considered significant" );
- printf( "\n /NOBLANKS default" );
- printf( "\n" );
- printf( "\n /LOOKAHEAD=n Lines to look ahead in each file to resync \
- after difference" );
- printf( "\n default = 200" );
- printf( "\n" );
- printf( "\n /SKIP1=n Pages in NEWFILE to skip before compare. Al\
- so sets /SKIP2" );
- printf( "\n default = 0" );
- printf( "\n" );
- printf( "\n /SKIP2=n Pages in OLDFILE to skip before compare. Mu\
- st be after /SKIP1" );
- printf( "\n default = 0" );
- printf( "\n" );
- }
-
- /*------------------------------------------------------------------------------
- OPEN_FILES - opens the input and output files.
- ------------------------------------------------------------------------------*/
- open_files()
- {
- int i;
-
- trace( "open_files" );
- for( i = 1; i < 3; i++ )
- if( (infile[ i ] = fopen( infile_name[ i ], "r")) == NULL )
- {
- printf( "\nError: Can't open %s", infile_name[ i ] );
- command_errors++;
- }
- if( files > 2 )
- if( (outfile = fopen( outfile_name, "w" )) == NULL )
- {
- printf( "\nError: Can't create %s", outfile_name );
- command_errors++;
- }
- ret;
- }
-
- /*------------------------------------------------------------------------------
- REDIRECT - performs output redirection under VAX 11 VMS.
- ------------------------------------------------------------------------------*/
- redirect( str )
- char *str;
- {
- char filename[ 132 ], *ptr, *dest;
-
- trace( "redirect" );
- dest = filename;
- if( (ptr = index( str, '=' ) + 1) == (char *)(NULL + 1) )
- {
- printf( "\nERROR in option %s", str );
- command_errors++;
- }
- while( (*ptr != OPT_FLAG) && ((*(dest++) = *(ptr++)) != '\0') );
- *dest = '\0';
- if( (msg = fopen( filename, "w" )) == NULL )
- {
- printf( "\nERROR creating %s", filename );
- command_errors++;
- }
- ret;
- }
-
- /*------------------------------------------------------------------------------
- STRIP_OPT - processes each command line option.
- ------------------------------------------------------------------------------*/
- strip_opt( str )
- char *str;
- {
- trace( "strip_opt" );
- upper( str );
- if( str[ 0 ] == OPT_FLAG )
- {
- if( match( str + 1, "BAR_COL" ) )
- bar_col = num( str );
- else if( match( str + 1, "TOP_SKIP" ) )
- top_skip = num( str );
- else if( match( str + 1, "BOT_SKIP" ) )
- bot_skip = num( str );
- else if( match( str + 1, "PAGE_LEN" ) )
- page_len = num( str );
- else if( match( str + 1, "UP_CASE" ) )
- up_case = 1;
- else if( match( str + 1, "NOUP_CASE" ) )
- up_case = 0;
- else if( match( str + 1, "RE_SYNC" ) )
- re_sync = num( str );
- else if( match( str + 1, "BLANKS" ) )
- blanks = 1;
- else if( match( str + 1, "NOBLANKS" ) )
- blanks = 0;
- else if( match( str + 1, "LOOKAHEAD" ) )
- lookahead = num( str );
- else if( match( str + 1, "SKIP1" ) )
- skip1 = skip2 = num( str );
- else if( match( str + 1, "SKIP2" ) )
- skip2 = num( str );
- #ifdef TRACER_FUNCTIONS
- else if( match( str + 1, "TRACE" ) )
- trace_enabled = debug = 1;
- #endif
- else if( match( str + 1, "OUTPUT" ) )
- redirect( str );
- else
- {
- printf( "\nUnrecognized Option: %s", str );
- command_errors++;
- }
- }
- else
- {
- switch( files )
- {
- case 0:
- strcpy( infile_name[ 1 ], str );
- break;
- case 1:
- strcpy( infile_name[ 2 ], str );
- break;
- case 2:
- strcpy( outfile_name, str );
- output = 1;
- break;
- default:
- printf( "\nError: Too many files at %s", str );
- command_errors++;
- break;
- }
- files++;
- }
- if( index( str + 1, OPT_FLAG ) != NULL )
- strip_opt( index( str + 1, OPT_FLAG ) );
- ret;
- }
-
- /*------------------------------------------------------------------------------
- UPPER - converts the string 'str' to upper case.
- ------------------------------------------------------------------------------*/
- upper( str )
- char *str;
- {
- trace( "upper" );
- for( ; ; )
- {
- if( *str == '\0' )
- ret
- *str = toupper( *str );
- str++;
- }
- }
-
- /*------------------------------------------------------------------------------
- MATCH - looks for a match of 'str' with the first (strlen( str) ) characters
- of 'pattern'. Returns 0 for no match, nonzero on match.
- ------------------------------------------------------------------------------*/
- int match( str, pattern )
- char *str, *pattern;
- {
- trace( "match" );
- for( ; ; )
- {
- if( *str != *pattern )
- ret_val( 0 )
- str++;
- pattern++;
- if( *pattern == '\0' )
- ret_val( 1 )
- if( *str == '\0' )
- ret_val( 1 )
- if( *str == '=' )
- ret_val( 1 )
- }
- }
-
- /*------------------------------------------------------------------------------
- NUM - returns the integer associated with a command line option. An equal
- sign must appear in the option.
- ------------------------------------------------------------------------------*/
- int num( str )
- char *str;
- {
- trace( "num" );
- if( index( str, '=' ) == NULL )
- ret_val( 0 )
- else
- ret_val( atoi( index( str, '=' ) + 1 ) )
- }
-
- #ifdef TRACER_FUNCTIONS
-
- char_ptr names[ 20 ];
- int stack = 0;
-
- callstack( str )
- char *str;
- {
- int i;
- char c;
-
- names[ stack++ ] = str;
- if( debug )
- {
- for( i = 0; i < stack; i++ )
- printf( " " );
- printf( "Entering %s\n", str );
- }
- #ifndef VAX11C
- if( trace_enabled && kbhit() )
- {
- switch( getch() )
- {
- case 't':
- case 'T':
- debug = !debug;
- break;
- case 's':
- case 'S':
- printf( "\n-----------" );
- for( i = stack - 1; i >= 0; i-- )
- printf( "\n%s", names[ i ] );
- printf( "\n-----------\n" );
- break;
- default:
- break;
- }
- }
- #endif
- }
-
- callpop()
- {
- int i;
- if( debug )
- {
- for( i = 0; i < stack; i++ )
- printf( " " );
- printf( "Exiting %s\n", names[ stack ] );
- }
- stack--;
- }
-
- #endif
-