diff.cc #1

  • //
  • guest/
  • perforce_software/
  • p4/
  • 2014-2/
  • diff/
  • diff.cc
  • View
  • Commits
  • Open Download .zip Download (9 KB)
/*
 * Copyright 1997 Perforce Software.  All rights reserved.
 *
 * This file is part of Perforce - the FAST SCM System.
 *
 * Diff code written by James Strickland, May 1997.
 */

/*
 * Diff output generators.
 */

#define NEED_TYPES
#define NEED_SLEEP
#define NEED_FILE

#include <stdhdrs.h>
#include <error.h>
#include <strbuf.h>
#include <readfile.h>
#include <filesys.h>
#include <charset.h>
#include <i18napi.h>
#include <charcvt.h>

#include "diffsp.h"
#include "diffan.h"
#include "diff.h"

// NT CRLF handling -- out output files are raw (binary) because our
// input files were raw (according to ReadFile).  So we have to handle
// the proper CR when we print our lines.

Diff::Diff()
{
	out = 0;
	spx = 0;
	spy = 0;
	diff = 0;
	flags = 0;
	lineType = LineTypeRaw;
	closeOut = 0;
	fastMaxD = 0;
	chunkCnt = 0;

# ifdef USE_CRLF
	newLines = "\r\n";
# else
	newLines = "\n";
# endif
}

Diff::~Diff()
{
	delete diff;
	delete spx;
	delete spy;
	if( closeOut ) fclose( out );
}



void
Diff::DiffUnifiedDeleteFile( FileSys *f, Error *e )
{
    StrBuf buf;

    int lineCount = 0;
    while( f->ReadLine( &buf, e ) )
        lineCount++;

    // ignore errors when diffing deleted files.
    if( e->Test() )
    {
        e->Clear();
        return;
    } 

    f->Seek( 0, e );
    fprintf( out, "@@ -1,%d +1,0 @@\n", lineCount );

    while( f->ReadLine( &buf, e ) )
        fprintf( out, "-%s\n", buf.Text() );

}
  

void
Diff::SetInput( FileSys *fx, FileSys *fy, const DiffFlags &flags, Error *e )
{
	// diff -h does it by word; see DiffWithFlags below.

	spx = new Sequence( fx, flags, e );
	this->flags = &flags;
	if( !e->Test() )
	    spy = new Sequence( fy, flags, e );

	if( e->Test() )
	    return;

	diff = new DiffAnalyze( spx, spy, fastMaxD );
}

void
Diff::SetOutput( const char *fout, Error *e )
{
	// NT CRLF handling: raw writes

#ifdef OS_NT
	// XXX Hate to have Windows specific code here but there is
	// no easy fix short of using FileSys here...
	if( CharSetApi::isUnicode((CharSetApi::CharSet)GlobalCharSet::Get()) )
	{
	    CharSetCvtUTF816 cvt;
	    int newlen = 0;

	    const char *wname = cvt.FastCvt( fout, strlen( fout ), &newlen );

	    if( newlen > ( MAX_PATH * 2 ) )
	    {
		SetLastError( ERROR_BUFFER_OVERFLOW );
		goto errorout;
	    }

	    if( cvt.LastErr() == CharSetCvt::NONE &&
		( out = _wfopen( (const wchar_t *)wname, L"wb" ) ) )
	        goto done;
	}
#endif
	// If wfopen() fails, we intentionally fall through.
	if( !( out = fopen( fout, "wb" ) ) )
	{
	errorout:
	    e->Sys( "write", fout );
	    return;
	}
done:

#ifdef OS_NT
	SetHandleInformation( (HANDLE)_get_osfhandle( fileno(out) ), 
		HANDLE_FLAG_INHERIT, 0 );
#endif

	closeOut = 1;
}

void
Diff::SetOutput( FILE *fout )
{
	out = fout;
	lineType = LineTypeLocal;
	newLines = "\n";
}

void
Diff::CloseOutput( Error *e )
{
	// If SetInput never opened a file, CloseOutput won't
	// touch it.  Normally, that means 'out' is stdout, but
	// if not, caveat caller.

	if( !closeOut )
	    return;

	// Flush and check for output error.
	// Don't stack this error against others.

	if( fflush( out ) < 0 || ferror( out ) )
	    if( !e->Test() )
		e->Sys( "write", "diff" );

	fclose( out );
	closeOut = 0;
}

void
Diff::Walker(
	const char *flags,
	Sequence *sp,
	LineNo sx, LineNo ex )
{
	sp->SeekLine( sx );

	bool lineEnd = true;

	for( ; sx < ex; ++sx ) {
	    fputs( flags, out );
	    lineEnd = sp->Dump( out, sx, sx + 1, lineType );
	}

	if( lineEnd == false && this->flags->type == DiffFlags::Unified ) 
	    fprintf(out, "\n\\ No newline at end of file\n" );
}

void
Diff::DiffWithFlags( const DiffFlags &flags )
{
	// allow 'c' and 'u' (as well as 'C' and 'U') to take a count.
	// if it breaks, it defaults to 0 (which is then treated as 3).

	switch( flags.type )
	{
	case DiffFlags::Normal:	 DiffNorm(); break;
	case DiffFlags::Context: DiffContext( flags.contextCount ); break;
	case DiffFlags::Unified: DiffUnified( flags.contextCount ); break;
	case DiffFlags::Summary: DiffSummary(); break;
	case DiffFlags::HTML: 	 DiffHTML(); break;
	case DiffFlags::Rcs:	 DiffRcs(); break;
	}
}

void		
Diff::DiffNorm()
{
	Snake *s = diff->GetSnake();
	Snake *t;

	for( ; t = s->next; s = t )
	{
	    /* Print edit operator */

	    char c;
	    LineNo nx = s->u, ny = s->v;

	    if( s->u < t->x && s->v < t->y )	c = 'c', ++nx, ++ny;
	    else if( s->u < t->x ) 		c = 'd', ++nx;
	    else if( s->v < t->y ) 		c = 'a', ++ny;
	    else				continue;

				    	fprintf( out, "%d", nx );
	    if( t->x > nx )		fprintf( out, ",%d", t->x );
				    	fprintf( out, "%c%d", c, ny );
	    if( t->y > ny )		fprintf( out, ",%d", t->y );
	    				fprintf( out, "%s", newLines );

	    /* Line lines that differ */

	    Walker( "< ", spx, s->u, t->x );

	    if( c == 'c' ) 		fprintf( out, "---%s", newLines );

	    Walker( "> ", spy, s->v, t->y );
	}
}

void
Diff::DiffRcs()
{
	Snake *s = diff->GetSnake();
	Snake *t;

	for( ; t = s->next; s = t )
	{
	    if( s->u < t->x )
	    {
		fprintf( out, "d%d %d%s", s->u + 1, t->x - s->u, newLines );
		chunkCnt++;
	    }
	    if( s->v < t->y )
	    {
		fprintf( out, "a%d %d%s", t->x, t->y - s->v, newLines );
		chunkCnt++;
		spy->SeekLine( s->v );
		spy->Dump( out, s->v, t->y, lineType );
	    }
	}
}

void
Diff::DiffHTML()
{
	Snake *s = diff->GetSnake();
	Snake *t;

	for( ; t = s->next; s = t )
	{
	    // Dump the common stuff

	    spx->SeekLine( s->x );
	    spy->SeekLine( s->v );
	    spx->Dump( out, s->x, s->u, lineType );

	    // Dump spx

	    fprintf( out, "<font color=red>" );
	    spx->Dump( out, s->u, t->x, lineType );

	    // dump spy

	    fprintf( out, "</font><font color=blue>" );
	    spy->Dump( out, s->v, t->y, lineType );

	    // Done

	    fprintf( out, "</font>" );
	}
}

/*
 * DiffUnified
 */

void
Diff::DiffUnified( int c )
{
	// s,t bound the diffs being displayed.
	// First we move t forward until the snake is bigger than
	// 2 * CONTEXT, then we display [s,t], then iterate for [t,...].

	// Remember: a snake is a matching chunk.  Diffs are inbetween
	// snakes, before the first snake, or after the last snake.

	if( c < 0 ) c = 3;
	Snake *s = diff->GetSnake();
	Snake *t;

	while( t = s->next )
	{
	    // Look for snake > 2 * CONTEXT

	    while( t->next && t->x + 2 * c >= t->u )
		t = t->next;

	    // Compute first/last lines of diff block

	    LineNo sx = s->u - c > 0 ? s->u - c : 0;
	    LineNo sy = s->v - c > 0 ? s->v - c : 0;
	    LineNo ex = t->x + c < spx->Lines() ? t->x + c : spx->Lines();
	    LineNo ey = t->y + c < spy->Lines() ? t->y + c : spy->Lines();

	    // Display [s,t]

	    fprintf( out, "@@ -%d,%d +%d,%d @@%s",
		 sx+1, ex-sx, sy+1, ey-sy, newLines );

	    // Now walk the snake between s,t, displaying the diffs

	    do
	    {
		LineNo nx = s->u;
		LineNo ny = s->v;

		Walker( " ", spx, sx, nx );

		s = s->next;
		sx = s->x;
		sy = s->y;

		Walker( "-", spx, nx, sx );
		Walker( "+", spy, ny, sy );
	    }
	    while( s != t );

	    // Display the tail of the chunk

	    Walker( " ", spx, sx, ex );
	}
}

/*
 * DiffContext
 */

void
Diff::DiffContext( int c )
{
	// s,t bound the diffs being displayed.
	// First we move t forward until the snake is bigger than
	// 2 * CONTEXT, then we display [s,t], then iterate for [t,...].

	// Remember: a snake is a matching chunk.  Diffs are inbetween
	// snakes, before the first snake, or after the last snake.

	if( c < 0 ) c = 3;
	Snake *s = diff->GetSnake();
	Snake *t;

	for( ; t = s->next; s = t )
	{
	    // Look for snake > 2 * CONTEXT

	    while( t->next && t->x + 2 * c >= t->u )
		t = t->next;

	    // Compute first/last lines of diff block

	    LineNo sx = s->u - c > 0 ? s->u - c : 0;
	    LineNo sy = s->v - c > 0 ? s->v - c : 0;
	    LineNo ex = t->x + c < spx->Lines() ? t->x + c : spx->Lines();
	    LineNo ey = t->y + c < spy->Lines() ? t->y + c : spy->Lines();

	    // Display [s,t]

	    fprintf( out, "***************%s", newLines );

	    // File 1

	    fprintf( out, "*** %d,%d ****%s", sx+1, ex, newLines );

	    // Now walk the snake between s,t, displaying the diffs

	    Snake *ss, *tt;

	    for( ss = s; ss != t; ss = tt )
	    {
		tt = ss->next;

		if( ss->u < tt->x )
		{
		    Walker( "  ", spx, sx, ss->u );
		    const char *mark = ss->v < tt->y ? "! " : "- ";
		    Walker( mark, spx, ss->u, tt->x );
		    sx = tt->x;
		}
	    }

	    if( s->u < sx )
		Walker( "  ", spx, sx, ex );

	    // File 2

	    fprintf( out, "--- %d,%d ----%s", sy+1, ey, newLines );

	    // Now walk the snake between s,t, displaying the diffs

	    for( ss = s; ss != t; ss = tt )
	    {
		tt = ss->next;

		if( ss->v < tt->y )
		{
		    Walker( "  ", spy, sy, ss->v );
		    const char *mark = ss->u < tt->x ? "! " : "+ ";
		    Walker( mark, spy, ss->v, tt->y );
		    sy = tt->y;
		}
	    }

	    if( s->v < sy )
		Walker( "  ", spy, sy, ey );
	}
}

/*
 * DiffSummary
 */

void
Diff::DiffSummary()
{
	Snake *s = diff->GetSnake();
	Snake *t;

	LineNo l_deleted = 0;
	LineNo l_added = 0;
	LineNo l_edited_in = 0;
	LineNo l_edited_out = 0;

	LineNo c_deleted = 0;
	LineNo c_added = 0;
	LineNo c_edited = 0;

	for( ; t = s->next; s = t )
	{
	    /* Print edit operator */

	    if( s->u < t->x && s->v < t->y ) 
	    {
		l_edited_in += (t->x - s->u);
		l_edited_out += (t->y - s->v);
		++c_edited;
	    } 
	    else if (s->v < t->y) 
	    {
	    	l_added += (t->y - s->v);
		++c_added;
	    } 
	    else if( s->u < t->x )
	    {
	    	l_deleted += (t->x - s->u);
		++c_deleted;
	    }
	}

	fprintf( out, 
		"add %d chunks %d lines\n"
		"deleted %d chunks %d lines\n"
		"changed %d chunks %d / %d lines\n",
			c_added, l_added,
			c_deleted, l_deleted,
			c_edited, l_edited_in, l_edited_out );
}
# Change User Description Committed
#1 15903 Matt Attaway Everything should be happy now between the Workshop and the depot paths
//guest/perforce_software/p4/2014_2/diff/diff.cc
#1 15901 Matt Attaway Clean up code to fit modern Workshop naming standards
//guest/perforce_software/p4/2014.2/diff/diff.cc
#1 12189 Matt Attaway Initial (and much belated) drop of 2014.2 p4 source code