diffmerge.cc #2

  • //
  • guest/
  • perforce_software/
  • p4/
  • 2017-1/
  • diff/
  • diffmerge.cc
  • View
  • Commits
  • Open Download .zip Download (29 KB)
/*
 * Copyright 1995, 2007 Perforce Software.  All rights reserved.
 *
 * This file is part of Perforce - the FAST SCM System.
 */

/*
 * diffmerge.cc - 3 way file merge
 *
 * Classes defined:
 *
 *	DiffMerge - control block for merging
 *
 * Public methods:
 *
 *	DiffMerge::DiffMerge() - Merge 3 files to produce integrated result
 *	DiffMerge::~DiffMerge() - dispose of DiffMerge and its contents
 *	DiffMerge::Read() - produce next part of integrated result
 *
 * Internal classes:
 *
 *	DiffFfile - line oriented ReadFile
 *	DiffDFile - a diff stream
 *
 * Private methods:
 *
 *	DiffDFile::DiffDFile() - start a diff(1) between the base and one leg
 *
 * History:
 *	2-18-97 (seiwald) - translated to C++.
 */

# define NEED_TYPES
# define NEED_FILE

# include <stdhdrs.h>

# include <error.h>
# include <debug.h>
# include <strbuf.h>
# include <filesys.h>

# include <readfile.h>

# include <diff.h>
# include <diffsp.h>
# include <diffan.h>
# include <diffmerge.h>

# define DEBUG_MERGE ( p4debug.GetLevel( DT_DIFF ) >= 3 )

/*
 * selbitTab - how to output each leg given the next pair of diffs
 *
 * DiffLeg represents the leg we're looking to output, and DiffDiffs
 * is how DiffDiff() said the next pair of diffs merged. 
 */

enum DiffLeg { DL_BASE, DL_LEG1, DL_LEG2, DL_LAST } ;

const int selbitTab[ DL_LAST ][ DD_LAST ] = {

/* BASE	at */ 	/* EOF */	0,		
		/* LEG1	*/	SEL_BASE|SEL_LEG2,	// base == leg2
		/* LEG2	*/	SEL_BASE|SEL_LEG1,	// base == leg1
		/* BOTH	*/	SEL_BASE,	
		/* CONF	*/	SEL_BASE|SEL_CONF,	
		/* ALL */	SEL_ALL,

/* LEG1	at */ 	/* EOF */	0,		
		/* LEG1	*/	SEL_LEG1|SEL_RSLT,
		/* LEG2	*/	0,			
		/* BOTH	*/	SEL_LEG1|SEL_LEG2|SEL_RSLT, // leg1 == leg2
		/* CONF	*/	SEL_LEG1|SEL_RSLT|SEL_CONF, // leg1 != leg2
		/* ALL */	0,

/* LEG2 at */ 	/* EOF */	0,
		/* LEG1	*/	0,
		/* LEG2	*/	SEL_LEG2|SEL_RSLT,
		/* BOTH	*/	0,			// == leg1
		/* CONF	*/	SEL_LEG2|SEL_RSLT|SEL_CONF, // leg1 != leg2
		/* ALL */	0,

};

/* 
 * MergeState - state for Read()'s state machine
 */

enum MergeState {
	MS_DIFFDIFF,	/* do diff diff */
	MS_BASE,	/* dumping the original */
	MS_LEG1,	/* dumping leg1 */
	MS_LEG2,	/* dumping leg2 */
	MS_DONE		/* return 0 */
} ;

/*
 * DiffDFile::DiffDFile() - popen(3) a diff(1) between the base and one leg
 */

inline LineNo dmin( LineNo a, LineNo b )  { return a < b ? a : b; }

/*
 * DiffFfile - line oriented ReadFile
 */

class DiffFfile : public Sequence { 

    public:
			DiffFfile( 
			    FileSys *file,
			    const DiffFlags &flags,
			    LineType lt,
			    Error *e ) 
			    : Sequence( file, flags, e )
			{ 
			    lineType = lt;
			    end = 0;
			}

    	LineLen  	MaxLineLength() const;

	LineType	lineType;

	/* Track our progress for output */

	LineNo		start;	/* first line to output */
	LineNo		end;	/* last line, and sync point */

} ;

/*
 * DiffDFile - a diff stream
 */

class DiffDFile : public DiffAnalyze {

    public:
			DiffDFile( DiffFfile *base, DiffFfile *leg )
			: DiffAnalyze( base, leg ) 
			{ 
				this->base = base;
				this->leg = leg;
				s = *GetSnake(); 
			}

	LineNo		StartLeg() { return s.y - leg->end; }
	LineNo		StartBase() { return s.x - base->end; }
	LineNo		EndLeg() { return s.v - leg->end; }
	LineNo		EndBase() { return s.u - base->end; }

	// alias the above for the leg1 leg2 snake ONLY

	LineNo		StartL1() { return StartBase(); }
	LineNo		StartL2() { return StartLeg(); }
	LineNo		EndL1() { return EndBase(); }
	LineNo		EndL2() { return EndLeg(); }

	void		AdvanceAndSync();

    public:

	Snake		s;		/* snake of differences */

    private:

	DiffFfile	*base;	/* for StartBase()/EndBase() */
	DiffFfile	*leg;	/* for StartLeg()/EndLeg() */

} ;

void
DiffDFile::AdvanceAndSync()
{
	// Advance

	while( s.next && ( EndLeg() <= 0 || EndBase() <= 0 ) ) 
	    s = *s.next;

	// Sync up to starting point

	LineNo adj = dmin( StartLeg(), StartBase() );
	if( adj < 0 ) { s.y += -adj; s.x += -adj; }
}

void
SnakeDump( const char *t, Snake *s )
{
	p4debug.printf( "snake %s\n", t );

	while( s )
	{
	    p4debug.printf("s %x %d %d %d %d\n", s, s->x, s->u, s->y, s->v );
	    s = s->next;
	}
}

/*
 * DiffMerge::DiffMerge() - Merge 3 files to produce integrated result
 */

DiffMerge::DiffMerge( 
	FileSys *base,
	FileSys *leg1,
	FileSys *leg2,
	const DiffFlags &flags,
	LineType lineType,
	Error *e )
{
	readFile = 0;
	emptyFile = 0;
	bf = lf1 = lf2 = 0;
	df1 = df2 = df3 = 0;
	diff3behavior = 0;
	oldMode = 0;
	
	switch( flags.grid )
	{
	case DiffFlags::Optimal:
	    gridType = GRT_OPTIMAL;
	    break;
	case DiffFlags::GuardedDiff3:
	    diff3behavior = 1;
	case DiffFlags::Guarded:
	    gridType = GRT_GUARDED;
	    break;
	case DiffFlags::Diff3:
	    diff3behavior = 1;
	    break;
	case DiffFlags::TwoWay:
	    gridType = GRT_TWOWAY;
	    emptyFile = FileSys::Create( FST_EMPTY );
	    break;
	}

	state = MS_DIFFDIFF;

	if( DEBUG_MERGE )
	    p4debug.printf( "merging %s x %s x %s\n", base->Name(),
	        leg1->Name(), leg2->Name() );

	/*
	**  Open the three input files.
	*/

	bf = new DiffFfile( emptyFile ? emptyFile : base, flags, lineType, e );
	lf1 = new DiffFfile( leg1, flags, lineType, e );
	lf2 = new DiffFfile( leg2, flags, lineType, e );

	if( e->Test() )
	    return;

	/*
	**  Fork off the two diffs, between the base and l1, and between
	**  base and l2.  Prime the diff file structures by reading the
	**  first two diffs.
	*/

	df1 = new DiffDFile( bf, lf1 );
	df2 = new DiffDFile( bf, lf2 );
	df3 = new DiffDFile( lf1, lf2 );

	if( DEBUG_MERGE )
	{
	    SnakeDump( "df1", &df1->s );
	    SnakeDump( "df2", &df2->s );
	    SnakeDump( "df3", &df3->s );
	}
}

/*
 * DiffMerge::~DiffMerge() - dispose of DiffMerge and its contents
 */

DiffMerge::~DiffMerge()
{
	delete bf;
	delete lf1;
	delete lf2;

	delete df1;
	delete df2;
	delete df3;

	delete emptyFile;
}

/*
 * DiffMerge::Read() - produce next part of integrated result
 */

int
DiffMerge::Read( char *buf, int len, int *outlen )
{
	for(;;)
	{
	    /*
	     * If readFile is set, we're still returning output from
	     * one of the file legs.  Keep doing so until the user's
	     * buffer is satisfied, or we've output this chunk.
	     */

	    if( readFile )
	    {
		*outlen = readFile->CopyLines( 
		    readFile->start, 
		    readFile->end,
		    buf, len, 
		    readFile->lineType );

		if( !*outlen )
		    readFile = 0;

		return selbits;
	    }

	    switch( state )
	    {
	    case MS_DIFFDIFF:	

		/* do diff diff */

		if( ( diffDiff = DiffDiff() ) == DD_EOF )
		{
		    state = MS_DONE;
		    break;
		}

	        if( diffDiff != DD_ALL)
		{
		    *outlen = 0;
		    state = MS_BASE;
		    return SEL_ALL;
		}

	    case MS_BASE:		/* dumping the original */

		if( selbits = selbitTab[ DL_BASE ][ diffDiff ] )
		{
		    readFile = bf;
		    readFile->SeekLine( bf->start );
		    state = MS_LEG1;
		    break;
		}

	    case MS_LEG1:		/* dumping leg1 */

		if( selbits = selbitTab[ DL_LEG1 ][ diffDiff ] )
		{
		    readFile = lf1;
		    readFile->SeekLine( lf1->start );
		    state = MS_LEG2;
		    break;
		}

	    case MS_LEG2:		/* dumping leg2 */

		if( selbits = selbitTab[ DL_LEG2 ][ diffDiff ] )
		{
		    readFile = lf2;
		    readFile->SeekLine( lf2->start );
		}

		state = MS_DIFFDIFF;
		break;

	    case MS_DONE:		/* return 0 */

		*outlen = 0;
		return 0;
	    }
	}
}

/*
 * DiffMerge::BitNames() - turn out mnemonic(?) names for the flags
 */

const char *
DiffMerge::BitNames( int bits )
{
	switch( bits )
	{
	case SEL_ALL:			return "ALL";
	case SEL_BASE:			return "BASE";
	case SEL_BASE|SEL_CONF:		return "BASE CONFLICT";
	case SEL_BASE|SEL_LEG1:		return "BASE L1";
	case SEL_BASE|SEL_LEG2:		return "BASE L2";
	case SEL_LEG1|SEL_RSLT|SEL_CONF:return "L1 CONFLICT";
	case SEL_LEG2|SEL_RSLT|SEL_CONF:return "L2 CONFLICT";
	case SEL_LEG1|SEL_RSLT:		return "L1";
	case SEL_LEG2|SEL_RSLT:		return "L2";
	case SEL_LEG1|SEL_LEG2|SEL_RSLT:return "L1 L2";
	default:			return "?";
	}
}

/*
 * DiffMerge::MaxLineLength() - find the maximum length of a line in any file
 */

LineLen 
DiffMerge::MaxLineLength() const
{
	LineLen l, m = bf->MaxLineLength();

	if( ( l = lf1->MaxLineLength() ) > m ) m = l;
	if( ( l = lf2->MaxLineLength() ) > m ) m = l;

	return m;
}

/*
 * DiffFfile::MaxLineLength() - find the maximum length of a line
 */

LineLen 
DiffFfile::MaxLineLength() const
{
	LineLen m = 0;

	for( LineNo l = 0; l < Lines(); l++ )
	    if( m < Length( l ) )
		 m = Length( l );

	return  m;
}

enum Mode {
	ALL, ALL1, ALL2, 
	BOTH, BOTHX, 
	BOTHX1, BOTHX2, CONF,
	DEL1, DEL2,
	DELB, DELB1, DELB2,
	EDIT1, EDIT2,
	EDITC1, EDITC2,
	INSX1, INSX2,
	INSX3, INSX4,
	INSY1, INSY2,
	INS12, INSX12, INSY12,
	INS1, INS2, 
	INSC1, INSC2, 
	INSC3, INSC4,
	INSC5, INSC6,
	INSC7, INSC8,
	NONE
} ;

const char *ModeNames[] = {
	"ALL", "ALL1", "ALL2", 
	"BOTH", "BOTHX", 
	"BOTHX1", "BOTHX2", "CONF",
	"DEL1", "DEL2", 
	"DELB", "DELB1", "DELB2", 
	"EDIT1", "EDIT2",
	"EDITC1", "EDITC2",
	"INSX1", "INSX2",
	"INSX3", "INSX4",
	"INSY1", "INSY2",
	"INS12", "INSX12", "INSY12",
	"INS1", "INS2", 
	"INSC1", "INSC2", 
	"INSC3", "INSC4",
	"INSC5", "INSC6",
	"INSC7", "INSC8",
	"NONE"
} ;

struct DiffGrid {

	Mode		mode;
	DiffDiffs	diffs;

} ;

const DiffGrid twoWayGrid[2][2][2][2][2][2] = {

//    ---- BASE/LEG1         ---- BASE/LEG2         ---- LEG1/LEG2
//    ||                     ||                     ||
//    __   __   __      __   __   _X      __   __   X_      __   __   XX  
//    __   _X   __      __   _X   _X      __   _X   X_      __   _X   XX  
//    __   X_   __      __   X_   _X      __   X_   X_      __   X_   XX  
//    __   XX   __      __   XX   _X      __   XX   X_      __   XX   XX  

      CONF,DD_CONF,     CONF,DD_CONF,     CONF,DD_CONF,     BOTH,DD_BOTH,
      CONF,DD_CONF,     CONF,DD_CONF,     CONF,DD_CONF,     BOTH,DD_BOTH,
      CONF,DD_CONF,     CONF,DD_CONF,     CONF,DD_CONF,     BOTH,DD_BOTH,
      CONF,DD_CONF,     CONF,DD_CONF,     CONF,DD_CONF,     BOTH,DD_BOTH,

//    _X   __   __      _X   __   _X      _X   __   X_      _X   __   XX  
//    _X   _X   __      _X   _X   _X      _X   _X   X_      _X   _X   XX  
//    _X   X_   __      _X   X_   _X      _X   X_   X_      _X   X_   XX  
//    _X   XX   __      _X   XX   _X      _X   XX   X_      _X   XX   XX  

      CONF,DD_CONF,     CONF,DD_CONF,     CONF,DD_CONF,     BOTH,DD_BOTH,
      CONF,DD_CONF,     CONF,DD_CONF,     CONF,DD_CONF,     BOTH,DD_BOTH,
      CONF,DD_CONF,     CONF,DD_CONF,     CONF,DD_CONF,     BOTH,DD_BOTH,
      CONF,DD_CONF,     CONF,DD_CONF,     CONF,DD_CONF,     BOTH,DD_BOTH,

//    X_   __   __      X_   __   _X      X_   __   X_      X_   __   XX  
//    X_   _X   __      X_   _X   _X      X_   _X   X_      X_   _X   XX  
//    X_   X_   __      X_   X_   _X      X_   X_   X_      X_   X_   XX  
//    X_   XX   __      X_   XX   _X      X_   XX   X_      X_   XX   XX  

      CONF,DD_CONF,     CONF,DD_CONF,     CONF,DD_CONF,     BOTH,DD_BOTH,
      CONF,DD_CONF,     CONF,DD_CONF,     CONF,DD_CONF,     BOTH,DD_BOTH,
      CONF,DD_CONF,     CONF,DD_CONF,     CONF,DD_CONF,     BOTH,DD_BOTH,
      CONF,DD_CONF,     CONF,DD_CONF,     CONF,DD_CONF,     BOTH,DD_BOTH,

//    XX   __   __      XX   __   _X      XX   __   X_      XX   __   XX  
//    XX   _X   __      XX   _X   _X      XX   _X   X_      XX   _X   XX  
//    XX   X_   __      XX   X_   _X      XX   X_   X_      XX   X_   XX  
//    XX   XX   __      XX   XX   _X      XX   XX   X_      XX   XX   XX  

      CONF,DD_CONF,     CONF,DD_CONF,     CONF,DD_CONF,     BOTH,DD_BOTH,
      CONF,DD_CONF,     CONF,DD_CONF,     CONF,DD_CONF,     BOTH,DD_BOTH,
      CONF,DD_CONF,     CONF,DD_CONF,     CONF,DD_CONF,     BOTH,DD_BOTH,
      CONF,DD_CONF,     CONF,DD_CONF,     CONF,DD_CONF,     BOTH,DD_BOTH
} ;

const DiffGrid optimalGrid[2][2][2][2][2][2] = {

//    ---- BASE/LEG1         ---- BASE/LEG2         ---- LEG1/LEG2
//    ||                     ||                     ||
//    __   __   __      __   __   _X      __   __   X_      __   __   XX  
//    __   _X   __      __   _X   _X      __   _X   X_      __   _X   XX  
//    __   X_   __      __   X_   _X      __   X_   X_      __   X_   XX  
//    __   XX   __      __   XX   _X      __   XX   X_      __   XX   XX  

      CONF,DD_CONF,     INSX1,DD_LEG1,    INSX2,DD_LEG2,    BOTHX,DD_BOTH,
      CONF,DD_CONF,     INSC6,DD_CONF,    INSX2,DD_LEG2,    BOTH,DD_BOTH,
      CONF,DD_CONF,     CONF,DD_CONF,     DELB,DD_BOTH,     DELB,DD_BOTH,
      EDITC1,DD_CONF,   EDIT1,DD_LEG1,    EDIT1,DD_LEG1,    ALL2,DD_ALL,

//    _X   __   __      _X   __   _X      _X   __   X_      _X   __   XX  
//    _X   _X   __      _X   _X   _X      _X   _X   X_      _X   _X   XX  
//    _X   X_   __      _X   X_   _X      _X   X_   X_      _X   X_   XX  
//    _X   XX   __      _X   XX   _X      _X   XX   X_      _X   XX   XX  

      CONF,DD_CONF,     INSX1,DD_LEG1,    INSC5,DD_CONF,    BOTH,DD_BOTH,
      INS12,DD_CONF,    INSX12,DD_CONF,   INSY12,DD_CONF,   BOTH,DD_BOTH,
      CONF,DD_CONF,     CONF,DD_CONF,     INSC3,DD_CONF,    INSC3,DD_CONF,
      INSX3,DD_LEG1,    INSX1,DD_LEG1,    INSC1,DD_CONF,    ALL2,DD_ALL,

//    X_   __   __      X_   __   _X      X_   __   X_      X_   __   XX  
//    X_   _X   __      X_   _X   _X      X_   _X   X_      X_   _X   XX  
//    X_   X_   __      X_   X_   _X      X_   X_   X_      X_   X_   XX  
//    X_   XX   __      X_   XX   _X      X_   XX   X_      X_   XX   XX  

      CONF,DD_CONF,     DELB,DD_BOTH,     CONF,DD_CONF,     DELB,DD_BOTH,
      CONF,DD_CONF,     INSC4,DD_CONF,    CONF,DD_CONF,     INSC4,DD_CONF,
      DELB,DD_BOTH,     DELB,DD_BOTH,     DELB,DD_BOTH,     DELB,DD_BOTH,
      DEL1,DD_LEG1,     DEL1,DD_LEG1,     INSC8,DD_CONF,    ALL2,DD_ALL,

//    XX   __   __      XX   __   _X      XX   __   X_      XX   __   XX  
//    XX   _X   __      XX   _X   _X      XX   _X   X_      XX   _X   XX  
//    XX   X_   __      XX   X_   _X      XX   X_   X_      XX   X_   XX  
//    XX   XX   __      XX   XX   _X      XX   XX   X_      XX   XX   XX  

      EDITC2,DD_CONF,   EDIT2,DD_LEG2,    EDIT2,DD_LEG2,    ALL1,DD_ALL,
      INSX4,DD_LEG2,    INSC2,DD_CONF,    INSX2,DD_LEG2,    ALL1,DD_ALL,
      DEL2,DD_LEG2,     INSC7,DD_CONF,    DEL2,DD_LEG2,     ALL1,DD_ALL,
      ALL,DD_ALL,       ALL,DD_ALL,       ALL,DD_ALL,       ALL,DD_ALL
} ;

const DiffGrid conservativeGrid[2][2][2][2][2][2] = {

//    ---- BASE/LEG1         ---- BASE/LEG2         ---- LEG1/LEG2
//    ||                     ||                     ||
//    __   __   __      __   __   _X      __   __   X_      __   __   XX  
//    __   _X   __      __   _X   _X      __   _X   X_      __   _X   XX  
//    __   X_   __      __   X_   _X      __   X_   X_      __   X_   XX  
//    __   XX   __      __   XX   _X      __   XX   X_      __   XX   XX  

      NONE, DD_ALL,     NONE, DD_ALL,     NONE, DD_ALL,     BOTHX,DD_CONF,
      NONE, DD_ALL,     NONE, DD_ALL,     NONE, DD_ALL,     BOTH,DD_CONF,
      NONE, DD_ALL,     NONE, DD_ALL,     NONE, DD_ALL,     NONE, DD_ALL,
      NONE, DD_ALL,     NONE, DD_ALL,     NONE, DD_ALL,     NONE, DD_ALL,

//    _X   __   __      _X   __   _X      _X   __   X_      _X   __   XX  
//    _X   _X   __      _X   _X   _X      _X   _X   X_      _X   _X   XX  
//    _X   X_   __      _X   X_   _X      _X   X_   X_      _X   X_   XX  
//    _X   XX   __      _X   XX   _X      _X   XX   X_      _X   XX   XX  

      NONE, DD_ALL,     NONE, DD_ALL,     NONE, DD_ALL,     BOTH,DD_CONF,
      NONE, DD_ALL,     NONE, DD_ALL,     NONE, DD_ALL,     BOTH,DD_CONF,
      NONE, DD_ALL,     NONE, DD_ALL,     NONE, DD_ALL,     BOTHX1,DD_CONF,
      NONE, DD_ALL,     NONE, DD_ALL,     NONE, DD_ALL,     NONE, DD_ALL,

//    X_   __   __      X_   __   _X      X_   __   X_      X_   __   XX  
//    X_   _X   __      X_   _X   _X      X_   _X   X_      X_   _X   XX  
//    X_   X_   __      X_   X_   _X      X_   X_   X_      X_   X_   XX  
//    X_   XX   __      X_   XX   _X      X_   XX   X_      X_   XX   XX  

      NONE, DD_ALL,     NONE, DD_ALL,     NONE, DD_ALL,     NONE, DD_ALL,
      NONE, DD_ALL,     NONE, DD_ALL,     NONE, DD_ALL,     BOTHX2,DD_CONF,
      NONE, DD_ALL,     NONE, DD_ALL,     NONE, DD_ALL,     NONE, DD_ALL,
      NONE, DD_ALL,     NONE, DD_ALL,     NONE, DD_ALL,     NONE, DD_ALL,

//    XX   __   __      XX   __   _X      XX   __   X_      XX   __   XX  
//    XX   _X   __      XX   _X   _X      XX   _X   X_      XX   _X   XX  
//    XX   X_   __      XX   X_   _X      XX   X_   X_      XX   X_   XX  
//    XX   XX   __      XX   XX   _X      XX   XX   X_      XX   XX   XX  

      NONE, DD_ALL,     NONE, DD_ALL,     NONE, DD_ALL,     NONE, DD_ALL,
      NONE, DD_ALL,     NONE, DD_ALL,     NONE, DD_ALL,     NONE, DD_ALL,
      NONE, DD_ALL,     NONE, DD_ALL,     NONE, DD_ALL,     NONE, DD_ALL,
      NONE, DD_ALL,     NONE, DD_ALL,     NONE, DD_ALL,     NONE, DD_ALL
} ;

DiffDiffs
DiffMerge::DiffDiff()
{
	/* All legs completely output? */

	if( bf->end == bf->Lines() &&
	    lf1->end == lf1->Lines() &&
	    lf2->end == lf2->Lines() )
		return( DD_EOF );

	/* Advance to next snake, if past old one. */

	df1->AdvanceAndSync();
	df2->AdvanceAndSync();
	df3->AdvanceAndSync();

	LineNo lb = 0, l1 = 0, l2 = 0;

	DiffGrid d;
	d.mode = NONE;

	// '-dg' (guarded) flag informs merge to use the conservative grid,
	// if no mode is returned then fallback to the optimal grid.

	// '-dt' (twoway) flag informs merge to use the twoway grid,

	if( gridType == GRT_TWOWAY ) d = twoWayGrid
		[ df1->StartLeg() == 0 ][ df1->StartBase() == 0 ]
		[ df2->StartLeg() == 0 ][ df2->StartBase() == 0 ]
		[ df3->StartL1() == 0 ][ df3->StartL2() == 0 ];

	if( gridType == GRT_GUARDED ) d = conservativeGrid
		[ df1->StartLeg() == 0 ][ df1->StartBase() == 0 ]
		[ df2->StartLeg() == 0 ][ df2->StartBase() == 0 ]
		[ df3->StartL1() == 0 ][ df3->StartL2() == 0 ];
	
	if( d.mode == NONE ) d = optimalGrid
		[ df1->StartLeg() == 0 ][ df1->StartBase() == 0 ]
		[ df2->StartLeg() == 0 ][ df2->StartBase() == 0 ]
		[ df3->StartL1() == 0 ][ df3->StartL2() == 0 ];

	Mode initialMode = d.mode;

	// Pre-process rules,  typically the outer snake information
	// contradicts the inner snake, its not perfect, but we use
	// the length of the snake to determine the best outcome.

	// Advance files

	switch( d.mode )
	{
	case INSC1:
	    // Trust outer, unless previous mode was INSX3
	    // and merge is trying to behave more like diff3

	    if( ( oldMode != INSX3 || !diff3behavior ) && 
	        ( df3->EndL1() > df2->EndBase() ) )
	    { d.mode = INSY2; d.diffs = DD_LEG2; break; }

	    d.mode = INS1; d.diffs = DD_LEG1;     // trust inner - insert L1
	    break;

	case INSC2:
	    // Trust outer, unless previous mode was INSX4
	    // and merge is trying to behave more like diff3

	    if( ( oldMode != INSX4 || !diff3behavior) && 
	        ( df3->EndL2() > df1->EndBase() ) )
	    { d.mode = INSY1; d.diffs = DD_LEG1; break; }

	    d.mode = INS2; d.diffs = DD_LEG2;     // trust inner = insert L1
	    break;

	case INSC3:
	    if( df3->EndL1() >= df1->EndLeg() )   // trust outer - delete base
	    { d.mode = DELB1; d.diffs = DD_BOTH; break; }

	    d.mode = INS1; d.diffs = DD_LEG1;     // trust inner - insert L1
	    break;

	case INSC4:
	    if( df3->EndL2() >= df2->EndLeg() )   // trust outer - delete base
	    { d.mode = DELB2; d.diffs = DD_BOTH; break; }

	    d.mode = INS2; d.diffs = DD_LEG2;     // trust inner - insert L2
	    break;

	case INSC5:
	    if( df3->EndL1() >= df1->EndLeg() )   // trust outer - delete base
	    { d.mode = DELB1; d.diffs = DD_BOTH; break; }

	    if( df3->EndL1() >= df1->EndBase() )  // trust outer - insert L2
	    { d.mode = INSX2; d.diffs = DD_LEG2; break; }

	    d.mode = INS1; d.diffs = DD_LEG1;     // trust inner - insert L1
	    break;

	case INSC6:
	    if( df3->EndL2() >= df2->EndLeg() )   // trust outer - delete base
	    { d.mode = DELB2; d.diffs = DD_BOTH; break; }

	    if( df3->EndL2() >= df2->EndBase() )  // trust outer - insert L1
	    { d.mode = INSX1; d.diffs = DD_LEG1; break; }

	    d.mode = INS2; d.diffs = DD_LEG2;     // trust inner - insert L2
	    break;

	case INSC7:
	   if( df1->EndBase() > df2->StartBase() ||   // overlap in delete
	       df2->EndBase() == df2->StartBase() )   // eof detection
	   { d.mode = DEL2; d.diffs = DD_LEG2; }
	   break;

	case INSC8:
	   if( df2->EndBase() > df1->StartBase() ||   // overlap in delete
	       df1->EndBase() == df1->StartBase() )   // eof detection
	   { d.mode = DEL1; d.diffs = DD_LEG1; }
	   break;

	case INSX12:
	    if( df3->StartL1() < df1->StartLeg() &&
	        df3->EndL1() >= df1->StartLeg() &&
	        df3->EndL2() >= df2->StartLeg() ) // trust outer - insert L1
	    { d.mode = INSY1; d.diffs = DD_LEG1; }
	    break;

	case INSY12:
	    if( df3->StartL2() < df2->StartLeg() &&
	        df3->EndL2() >= df2->StartLeg() &&
	        df3->EndL1() >= df1->StartLeg() ) // trust outer - insert L2
	    { d.mode = INSY2; d.diffs = DD_LEG2; }
	    break;
	}

	oldMode = d.mode;

	switch( d.mode )
	{
	case ALL1:
	    lb = l1 = l2 = dmin( df1->EndBase(), df3->EndL1() );
	    break;

	case ALL2:
	    lb = l1 = l2 = dmin( df2->EndBase(), df3->EndL2() );
	    break;

	case ALL:
	    lb = l1 = l2 = dmin( df1->EndBase(), df2->EndBase() );
	    break;

	case BOTH:
	    lb = dmin( df1->StartBase(), df2->StartBase() );
	    l1 = dmin( df1->StartLeg(), df3->EndL1() );
	    l2 = dmin( df2->StartLeg(), df3->EndL2() );
	    l1 = l2 = dmin( l1, l2 );
	    break;

	case BOTHX:
	    lb = dmin( df1->StartBase(), df2->StartBase() );
	    l1 = dmin( df1->StartLeg(), df3->EndL1() );
	    l2 = dmin( df2->StartLeg(), df3->EndL2() );
	    l1 = l2 = dmin( l1, l2 );
	    lb = dmin( lb, l1 );
	    d.mode = BOTH;
	    break;

	case BOTHX1:
	    l1 = l2 = dmin( df1->StartLeg(), df3->EndL1() );
	    d.mode = BOTH;
	    break;

	case BOTHX2:
	    l1 = l2 = dmin( df2->StartLeg(), df3->EndL2() );
	    d.mode = BOTH;
	    break;

	case DELB:
	    lb = dmin( df1->StartBase(), df2->StartBase() );
	    break;

	case DELB1:
	    lb = df1->EndBase();
	    break;

	case DELB2:
	    lb = df2->EndBase();
	    break;

	case DEL1:
	    lb = l2 = dmin( df1->StartBase(), df2->EndBase() );
	    break;

	case DEL2:
	    lb = l1 = dmin( df2->StartBase(), df1->EndBase() );
	    break;

	case EDIT1:
	case EDITC1:
	    l1 = dmin( df1->StartLeg(), df3->StartL1() );
	    lb = l2 = dmin( df1->StartBase(), df2->EndBase() );
	    break;

	case EDIT2:
	case EDITC2:
	    l2 = dmin( df2->StartLeg(), df3->StartL2() );
	    lb = l1 = dmin( df1->EndBase(), df2->StartBase() );
	    break;

	case INSX1:
	case INSX3:
	    l1 = dmin( df1->StartLeg(), df3->StartL1() );
	    break;

	case INSX2:
	case INSX4:
	    l2 = dmin( df2->StartLeg(), df3->StartL2() );
	    break;

	case INSY1:
	    l1 = df3->StartL1();
	    break;

	case INSY2:
	    l2 = df3->StartL2();
	    break;

	case INS1:
	    l1 = df1->StartLeg();
	    break;

	case INS2:
	    l2 = df2->StartLeg();
	    break;

	case INSX12:
	    l1 = dmin( df1->StartLeg(), df3->StartL1() );
	    l2 = df2->StartLeg();
	    break;

	case INSY12:
	    l1 = df1->StartLeg();
	    l2 = dmin( df2->StartLeg(), df3->StartL2() );
	    break;

	case CONF:
	    lb = dmin( df1->StartBase(), df2->StartBase() );
	    // fall through

	case INS12:
	    l1 = dmin( df1->StartLeg(), df3->StartL1() );
	    l2 = dmin( df2->StartLeg(), df3->StartL2() );
	    break;
	}

	if( DEBUG_MERGE )
	{
	    p4debug.printf("\nMMMM df1(%d,%d)B(%d,%d)L df2(%d,%d)B(%d,%d)L ",
	        df1->StartBase(),df1->EndBase(),df1->StartLeg(),df1->EndLeg(),
	        df2->StartBase(),df2->EndBase(),df2->StartLeg(),df2->EndLeg());

	    p4debug.printf("df3(%d,%d)L1(%d,%d)L2 [%c%c %c%c %c%c]\n",
	        df3->StartL1(),df3->EndL1(),df3->StartL2(),df3->EndL2(),
	        df1->StartLeg() == 0 ? 'X' : '_', 
	        df1->StartBase() == 0 ? 'X' : '_',
	        df2->StartLeg() == 0 ? 'X' : '_',
	        df2->StartBase() == 0 ? 'X' : '_',
	        df3->StartL1() == 0 ? 'X' : '_', 
	        df3->StartL2() == 0 ? 'X' : '_');

	    p4debug.printf("MMMM %s%s%s advancing %+d,%+d,%+d\n", 
	        ModeNames[ initialMode ], 
	        d.mode != initialMode ? " -> " : "",
	        d.mode != initialMode ? ModeNames[ d.mode ] : "",
	        lb, l1, l2 );
	}

	/* Set output range (start/end).  End is new sync point. */

	bf->start = bf->end;
	lf1->start = lf1->end;
	lf2->start = lf2->end;

	bf->end += lb;
	lf1->end += l1;
	lf2->end += l2;

	/* If an action may be downgraded from a conflict (currently only
	 * EDITC1 and EDITC2 fit that category), make sure they actually get
	 * through the processing or downgrade it now.
	 *
	 * 2007.2  Added BOTH (guarded mode)
	 */

	if( bf->end == bf->Lines() &&
	    lf1->end == lf1->Lines() &&
	    lf2->end == lf2->Lines() )
	{
	    if( d.mode == EDITC1 ) d.diffs = DD_LEG1; 
	    if( d.mode == EDITC2 ) d.diffs = DD_LEG2; 
	    if( d.mode == BOTH ) d.diffs = DD_BOTH; 
	}

	/* Handle Conflicts (post processing) 
	 * 
	 * Sometimes it is necessary to extend the conflict area to produce
	 * the best conflict.  Advance all snakes to the conflict end region 
	 * and see what happens next.  Depending  on the result,  we may grow 
	 * the conflict several times before returning.
	 *
	 * 2007.2 added the guarded flag (-dg) which uses the conservative 
	 * grid to change the action of "BOTH" processing.  Note that during
	 * conflict extension only (-dg) processed files will see "BOTH" as
	 * the conflict mode.
	 */

	DiffGrid n;
	int extendConflict = 0;

	while( d.diffs == DD_CONF && ( bf->end != bf->Lines() || 
	                               lf1->end != lf1->Lines() || 
	                               lf2->end != lf2->Lines() ) )
	{
	    LineNo ab = 0, a1 = 0, a2 = 0;
	    oldMode = 0;

	    // Conflict caused by inserting on L1 and L2 can never extend
	    // the base.

	    int noBaseAdvance = ( d.mode == INS12 
	                       || d.mode == INSX12 
	                       || d.mode == INSY12 );

	    df1->AdvanceAndSync();
	    df2->AdvanceAndSync();
	    df3->AdvanceAndSync();

	    n = optimalGrid
	        [ df1->StartLeg() == 0 ][ df1->StartBase() == 0 ]
		[ df2->StartLeg() == 0 ][ df2->StartBase() == 0 ]
		[ gridType == GRT_GUARDED && df3->StartL1() == 0 
	                                  && df3->StartL2() == 0 ]
		[ gridType == GRT_GUARDED && df3->StartL1() == 0 
	                                  && df3->StartL2() == 0 ];
	
	    switch( n.mode )
	    {
	    case INSX1: 
	    case INSX3: 
	        if( d.mode == INSC7 && extendConflict < 2 )
	        { d.diffs = DD_LEG2; break; }
	    case INS1:
	        a1 = df1->StartLeg();
	        break;
	    case INSX2: 
	    case INSX4: 
	        if( d.mode == INSC8 && extendConflict < 2 )
	        { d.diffs = DD_LEG1; break; }
	    case INS2:
	        a2 = df2->StartLeg();
	        break;
	    case DEL1:
	        ab = a2 = dmin( df1->StartBase(), df2->EndBase() );
	        break;
	    case DEL2: 
	        ab = a1 = dmin( df2->StartBase(), df1->EndBase() );
	        break;
	    case DELB:
	        // Prior to adding EDITC1 and EDITC2,  DELB would not 
	        // extend the conflict area, lets keep it that way.
	        // Added INSC7 and INSC8 for 2010.2.
	        if( d.mode != EDITC1 && d.mode != EDITC2 && 
	            d.mode != INSC7  && d.mode != INSC8 )
	            break;
	    case INSC3:
	    case INSC4:
	    case CONF:
	        ab = dmin( df1->StartBase(), df2->StartBase() );
	    case INS12:
	        if( d.mode == BOTH )
	        { 
	            a1 = df1->StartLeg(); 
	            a2 = df2->StartLeg(); 
	            break;
	        }

	        if( !noBaseAdvance )
	        {
	            a1 = dmin( df1->StartLeg(), df3->StartL1() );
	            a2 = dmin( df2->StartLeg(), df3->StartL2() );
	            break;
	        }

	        // INS12,INSX12,INSY12 (conflict) made a bad decision and 
	        // went with an outer snake,  since we are still in the 
	        // inner snake/ make up for that now by advancing to 
	        // inner startlegs.

	        if( df1->StartLeg() > df3->EndL1() &&
	            df2->StartLeg() > df3->EndL2() )
	        { 
	            a1 = df1->StartLeg(); 
	            a2 = df2->StartLeg(); 
	        }
	        break;
	    case EDITC1:
	        if( gridType == GRT_GUARDED ) 
	        {
	            a1 = dmin( df1->StartLeg(), df3->StartL1() );
	            ab = a2 = dmin( df1->StartBase(), df2->EndBase() );
	        }
	        break;
	    case EDITC2:
	        if( gridType == GRT_GUARDED )
	        {
	            a2 = dmin( df2->StartLeg(), df3->StartL2() );
	            ab = a1 = dmin( df1->EndBase(), df2->StartBase() );
	        }
	        break;
	    case BOTH:
	        a1 = dmin( df1->StartLeg(), df3->EndL1() );
	        a2 = dmin( df2->StartLeg(), df3->EndL2() );
	        break;

	    default:
	        break;
	    }

	    // if EDITC (conflict)  doesn't advance in the deleted leg
	    // then this isn't a conflict (fall back to edit).  
	    // if BOTH (conflict)  doesn't advance then fall back to both.

	    if( !extendConflict )
	    {
	        switch( d.mode )
	        {
	        case EDITC1:  if( !ab && !a2) d.diffs = DD_LEG1;
	                      break;
	        case EDITC2:  if( !ab && !a1) d.diffs = DD_LEG2;
	                      break;
	        case BOTH:    if( !ab && !a1 && !a2 ) d.diffs = DD_BOTH;
	                      break;
	        }
	    }

	    // INS12 (conflict) cannot advance base - bail
	    
	    if( noBaseAdvance && ab ) 
	        break;

	    // No conflict extension  - bail

	    if( !ab && !a1 && !a2 )
	        break;

	    // No longer a conflict - bail

	    if( d.diffs != DD_CONF )
	        break;

	    if( DEBUG_MERGE )
	        p4debug.printf("MMMM %s/%s adjustment %+d,%+d,%+d\n",
	            ModeNames[ d.mode ], ModeNames[ n.mode ], ab, a1, a2 );

	    bf->end += ab;
	    lf1->end += a1;
	    lf2->end += a2;

	    extendConflict++;
	}

	return d.diffs;
}

# Change User Description Committed
#2 23015 Nick Poole Update 2017.1 code drop
#1 22288 mark_mears import 2017.1 code drop