The DiffMerge Source Code

Read this page along with Code in Motion, a chapter from the book, Beautiful Code, (O'Reilly, 2007).

   1:  /*
   2:   * Copyright 1995, 2007 Perforce Software.  All rights reserved.
   3:   *
   4:   * This file is part of Perforce - the FAST SCM System.
   5:   */
   6:   
   7:  /*
   8:   * diffmerge.cc - 3 way file merge
   9:   *
  10:   * Classes defined:
  11:   *
  12:   *      DiffMerge - control block for merging
  13:   *
  14:   * Public methods:
  15:   *
  16:   *      DiffMerge::DiffMerge() - Merge 3 files to produce integrated result
  17:   *      DiffMerge::~DiffMerge() - dispose of DiffMerge and its contents
  18:   *      DiffMerge::Read() - produce next part of integrated result
  19:   *
  20:   * Internal classes:
  21:   *
  22:   *      DiffFfile - line oriented ReadFile
  23:   *      DiffDFile - a diff stream
  24:   *
  25:   * Private methods:
  26:   *
  27:   *      DiffDFile::DiffDFile() - start a diff(1) between the base and one leg
  28:   *
  29:   * History:
  30:   *      2-18-97 (seiwald) - translated to C++.
  31:   */
  32:   
  33:  # define NEED_TYPES
  34:  # define NEED_FILE
  35:   
  36:  # include <stdhdrs.h>
  37:   
  38:  # include <error.h>
  39:  # include <debug.h>
  40:  # include <strbuf.h>
  41:  # include <filesys.h>
  42:   
  43:  # include <readfile.h>
  44:   
  45:  # include <diff.h>
  46:  # include <diffsp.h>
  47:  # include <diffan.h>
  48:  # include <diffmerge.h>
  49:   
  50:  # define DEBUG_MERGE ( p4debug.GetLevel( DT_DIFF ) >= 3 )
  51:   
  52:  /*
  53:   * selbitTab - how to output each leg given the next pair of diffs
  54:   *
  55:   * DiffLeg represents the leg we're looking to output, and DiffDiffs
  56:   * is how DiffDiff() said the next pair of diffs merged. 
  57:   */
  58:   
  59:  enum DiffLeg { DL_BASE, DL_LEG1, DL_LEG2, DL_LAST } ;
  60:   
  61:  const int selbitTab[ DL_LAST ][ DD_LAST ] = {
  62:   
  63:  /* BASE at */   /* EOF */       0,              
  64:                  /* LEG1 */      SEL_BASE|SEL_LEG2,      // base == leg2
  65:                  /* LEG2 */      SEL_BASE|SEL_LEG1,      // base == leg1
  66:                  /* BOTH */      SEL_BASE,       
  67:                  /* CONF */      SEL_BASE|SEL_CONF,      
  68:                  /* ALL */       SEL_ALL,
  69:   
  70:  /* LEG1 at */   /* EOF */       0,              
  71:                  /* LEG1 */      SEL_LEG1|SEL_RSLT,
  72:                  /* LEG2 */      0,                      
  73:                  /* BOTH */      SEL_LEG1|SEL_LEG2|SEL_RSLT, // leg1 == leg2
  74:                  /* CONF */      SEL_LEG1|SEL_RSLT|SEL_CONF, // leg1 != leg2
  75:                  /* ALL */       0,
  76:   
  77:  /* LEG2 at */   /* EOF */       0,
  78:                  /* LEG1 */      0,
  79:                  /* LEG2 */      SEL_LEG2|SEL_RSLT,
  80:                  /* BOTH */      0,                      // == leg1
  81:                  /* CONF */      SEL_LEG2|SEL_RSLT|SEL_CONF, // leg1 != leg2
  82:                  /* ALL */       0,
  83:   
  84:  };
  85:   
  86:  /* 
  87:   * MergeState - state for Read()'s state machine
  88:   */
  89:   
  90:  enum MergeState {
  91:          MS_DIFFDIFF,    /* do diff diff */
  92:          MS_BASE,        /* dumping the original */
  93:          MS_LEG1,        /* dumping leg1 */
  94:          MS_LEG2,        /* dumping leg2 */
  95:          MS_DONE         /* return 0 */
  96:  } ;
  97:   
  98:  /*
  99:   * DiffDFile::DiffDFile() - popen(3) a diff(1) between the base and one leg
 100:   */
 101:   
 102:  inline int dmin( int a, int b )  { return a < b ? a : b; }
 103:   
 104:  /*
 105:   * DiffFfile - line oriented ReadFile
 106:   */
 107:   
 108:  class DiffFfile : public Sequence { 
 109:   
 110:      public:
 111:                          DiffFfile( 
 112:                              FileSys *file,
 113:                  const DiffFlags &flags,
 114:                              LineType lt,
 115:                              Error *e ) 
 116:                              : Sequence( file, flags, e )
 117:                          { 
 118:                              lineType = lt;
 119:                              end = 0;
 120:                          }
 121:   
 122:          size_t          MaxLineLength() const;
 123:   
 124:          LineType        lineType;
 125:   
 126:          /* Track our progress for output */
 127:   
 128:          int             start;  /* first line to output */
 129:          int             end;    /* last line, and sync point */
 130:   
 131:  } ;
 132:   
 133:  /*
 134:   * DiffDFile - a diff stream
 135:   */
 136:   
 137:  class DiffDFile : public DiffAnalyze {
 138:   
 139:      public:
 140:                          DiffDFile( DiffFfile *base, DiffFfile *leg )
 141:                          : DiffAnalyze( base, leg ) 
 142:                          { 
 143:                                  this->base = base;
 144:                                  this->leg = leg;
 145:                                  s = *GetSnake(); 
 146:                          }
 147:   
 148:          int             StartLeg() { return s.y - leg->end; }
 149:          int             StartBase() { return s.x - base->end; }
 150:          int             EndLeg() { return s.v - leg->end; }
 151:          int             EndBase() { return s.u - base->end; }
 152:   
 153:          // alias the above for the leg1 leg2 snake ONLY
 154:   
 155:          int             StartL1() { return StartBase(); }
 156:          int             StartL2() { return StartLeg(); }
 157:          int             EndL1() { return EndBase(); }
 158:          int             EndL2() { return EndLeg(); }
 159:   
 160:          void            AdvanceAndSync();
 161:   
 162:      public:
 163:   
 164:          Snake           s;              /* snake of differences */
 165:   
 166:      private:
 167:   
 168:          DiffFfile       *base;  /* for StartBase()/EndBase() */
 169:          DiffFfile       *leg;   /* for StartLeg()/EndLeg() */
 170:   
 171:  } ;
 172:   
 173:  void
 174:  DiffDFile::AdvanceAndSync()
 175:  {
 176:          // Advance
 177:   
 178:          while( s.next && ( EndLeg() <= 0 || EndBase() <= 0 ) ) 
 179:              s = *s.next;
 180:   
 181:          // Sync up to starting point
 182:   
 183:          int adj = dmin( StartLeg(), StartBase() );
 184:          if( adj < 0 ) { s.y += -adj; s.x += -adj; }
 185:  }
 186:   
 187:  void
 188:  SnakeDump( const char *t, Snake *s )
 189:  {
 190:          printf( "snake %s\n", t );
 191:   
 192:          while( s )
 193:          {
 194:              printf("s %x %d %d %d %d\n", s, s->x, s->u, s->y, s->v );
 195:              s = s->next;
 196:          }
 197:  }
 198:   
 199:  /*
 200:   * DiffMerge::DiffMerge() - Merge 3 files to produce integrated result
 201:   */
 202:   
 203:  DiffMerge::DiffMerge( 
 204:          FileSys *base,
 205:          FileSys *leg1,
 206:          FileSys *leg2,
 207:      const DiffFlags &flags,
 208:          LineType lineType,
 209:          Error *e )
 210:  {
 211:          readFile = 0;
 212:          bf = lf1 = lf2 = 0;
 213:          df1 = df2 = df3 = 0;
 214:   
 215:          state = MS_DIFFDIFF;
 216:   
 217:          if( DEBUG_MERGE )
 218:              printf( "merging %s x %s x %s\n", base->Name(),
 219:                  leg1->Name(), leg2->Name() );
 220:   
 221:          /*
 222:          **  Open the three input files.
 223:          */
 224:   
 225:          bf = new DiffFfile( base, flags, lineType, e );
 226:          lf1 = new DiffFfile( leg1, flags, lineType, e );
 227:          lf2 = new DiffFfile( leg2, flags, lineType, e );
 228:   
 229:          if( e->Test() )
 230:              return;
 231:   
 232:          /*
 233:          **  Fork off the two diffs, between the base and l1, and between
 234:          **  base and l2.  Prime the diff file structures by reading the
 235:          **  first two diffs.
 236:          */
 237:   
 238:          df1 = new DiffDFile( bf, lf1 );
 239:          df2 = new DiffDFile( bf, lf2 );
 240:          df3 = new DiffDFile( lf1, lf2 );
 241:   
 242:          if( DEBUG_MERGE )
 243:          {
 244:          SnakeDump( "df1", &df1->s );
 245:          SnakeDump( "df2", &df2->s );
 246:          SnakeDump( "df3", &df3->s );
 247:          }
 248:  }
 249:   
 250:  /*
 251:   * DiffMerge::~DiffMerge() - dispose of DiffMerge and its contents
 252:   */
 253:   
 254:  DiffMerge::~DiffMerge()
 255:  {
 256:          delete bf;
 257:          delete lf1;
 258:          delete lf2;
 259:   
 260:          delete df1;
 261:          delete df2;
 262:          delete df3;
 263:  }
 264:   
 265:  /*
 266:   * DiffMerge::Read() - produce next part of integrated result
 267:   */
 268:   
 269:  int
 270:  DiffMerge::Read( char *buf, int len, int *outlen )
 271:  {
 272:          for(;;)
 273:          {
 274:              /*
 275:               * If readFile is set, we're still returning output from
 276:               * one of the file legs.  Keep doing so until the user's
 277:               * buffer is satisfied, or we've output this chunk.
 278:               */
 279:   
 280:              if( readFile )
 281:              {
 282:                  *outlen = readFile->CopyLines( 
 283:                      readFile->start, 
 284:                      readFile->end,
 285:                      buf, len, 
 286:                      readFile->lineType );
 287:   
 288:                  if( !*outlen )
 289:                      readFile = 0;
 290:   
 291:                  return selbits;
 292:              }
 293:   
 294:              switch( state )
 295:              {
 296:              case MS_DIFFDIFF:   
 297:   
 298:                  /* do diff diff */
 299:   
 300:                  if( ( diffDiff = DiffDiff() ) == DD_EOF )
 301:                  {
 302:                      state = MS_DONE;
 303:                      break;
 304:                  }
 305:   
 306:                  if( diffDiff != DD_ALL)
 307:                  {
 308:                      *outlen = 0;
 309:                      state = MS_BASE;
 310:                      return SEL_ALL;
 311:                  }
 312:   
 313:              case MS_BASE:               /* dumping the original */
 314:   
 315:                  if( selbits = selbitTab[ DL_BASE ][ diffDiff ] )
 316:                  {
 317:                      readFile = bf;
 318:                      readFile->SeekLine( bf->start );
 319:                      state = MS_LEG1;
 320:                      break;
 321:                  }
 322:   
 323:              case MS_LEG1:               /* dumping leg1 */
 324:   
 325:                  if( selbits = selbitTab[ DL_LEG1 ][ diffDiff ] )
 326:                  {
 327:                      readFile = lf1;
 328:                      readFile->SeekLine( lf1->start );
 329:                      state = MS_LEG2;
 330:                      break;
 331:                  }
 332:   
 333:              case MS_LEG2:               /* dumping leg2 */
 334:   
 335:                  if( selbits = selbitTab[ DL_LEG2 ][ diffDiff ] )
 336:                  {
 337:                      readFile = lf2;
 338:                      readFile->SeekLine( lf2->start );
 339:                  }
 340:   
 341:                  state = MS_DIFFDIFF;
 342:                  break;
 343:   
 344:              case MS_DONE:               /* return 0 */
 345:   
 346:                  *outlen = 0;
 347:                  return 0;
 348:              }
 349:          }
 350:  }
 351:   
 352:  /*
 353:   * DiffMerge::BitNames() - turn out mnemonic(?) names for the flags
 354:   */
 355:   
 356:  const char *
 357:  DiffMerge::BitNames( int bits )
 358:  {
 359:          switch( bits )
 360:          {
 361:          case SEL_ALL:                   return "ALL";
 362:          case SEL_BASE:                  return "BASE";
 363:          case SEL_BASE|SEL_CONF:         return "BASE CONFLICT";
 364:          case SEL_BASE|SEL_LEG1:         return "BASE L1";
 365:          case SEL_BASE|SEL_LEG2:         return "BASE L2";
 366:          case SEL_LEG1|SEL_RSLT|SEL_CONF:return "L1 CONFLICT";
 367:          case SEL_LEG2|SEL_RSLT|SEL_CONF:return "L2 CONFLICT";
 368:          case SEL_LEG1|SEL_RSLT:         return "L1";
 369:          case SEL_LEG2|SEL_RSLT:         return "L2";
 370:          case SEL_LEG1|SEL_LEG2|SEL_RSLT:return "L1 L2";
 371:          default:                        return "?";
 372:          }
 373:  }
 374:   
 375:  /*
 376:   * DiffMerge::MaxLineLength() - find the maximum length of a line in any file
 377:   */
 378:   
 379:  size_t DiffMerge::MaxLineLength() const
 380:  {
 381:          size_t l, m = bf->MaxLineLength();
 382:   
 383:          if( ( l = lf1->MaxLineLength() ) > m ) m = l;
 384:          if( ( l = lf2->MaxLineLength() ) > m ) m = l;
 385:   
 386:          return m;
 387:  }
 388:   
 389:  /*
 390:   * DiffFfile::MaxLineLength() - find the maximum length of a line
 391:   */
 392:   
 393:  size_t 
 394:  DiffFfile::MaxLineLength() const
 395:  {
 396:          size_t m = 0;
 397:   
 398:          for( int l = 0; l < Lines(); l++ )
 399:              if( m < Length( l ) )
 400:                   m = Length( l );
 401:   
 402:          return  m;
 403:  }
 404:   
 405:  enum Mode {
 406:          ALL, ALL1, ALL2, 
 407:          BOTH, BOTHX, CONF,
 408:          DEL1, DEL2,
 409:          DELB, DELB1, DELB2,
 410:          EDIT1, EDIT2,
 411:          EDITC1, EDITC2,
 412:          INSX1, INSX2,
 413:          INSY1, INSY2,
 414:          INS12, INSX12, INSY12,
 415:          INS1, INS2, 
 416:          INSC1, INSC2, 
 417:          INSC3, INSC4,
 418:          INSC5, INSC6
 419:  } ;
 420:   
 421:  const char *ModeNames[] = {
 422:          "ALL", "ALL1", "ALL2", 
 423:          "BOTH", "BOTHX", "CONF",
 424:          "DEL1", "DEL2", 
 425:          "DELB", "DELB1", "DELB2", 
 426:          "EDIT1", "EDIT2",
 427:          "EDITC1", "EDITC2",
 428:          "INSX1", "INSX2",
 429:          "INSY1", "INSY2",
 430:          "INS12", "INSX12", "INSY12",
 431:          "INS1", "INS2", 
 432:          "INSC1", "INSC2", 
 433:          "INSC3", "INSC4",
 434:          "INSC5", "INSC6"
 435:  } ;
 436:   
 437:  const struct DiffGrid {
 438:   
 439:          Mode            mode;
 440:          DiffDiffs       diffs;
 441:   
 442:  } diffGrid[2][2][2][2][2][2] = {
 443:   
 444:  //    ---- BASE/LEG1         ---- BASE/LEG2         ---- LEG1/LEG2
 445:  //    ||                     ||                     ||
 446:  //    __   __   __      __   __   _X      __   __   X_      __   __   XX  
 447:  //    __   _X   __      __   _X   _X      __   _X   X_      __   _X   XX  
 448:  //    __   X_   __      __   X_   _X      __   X_   X_      __   X_   XX  
 449:  //    __   XX   __      __   XX   _X      __   XX   X_      __   XX   XX  
 450:   
 451:        CONF,DD_CONF,     INSX1,DD_LEG1,    INSX2,DD_LEG2,    BOTHX,DD_BOTH,
 452:        CONF,DD_CONF,     INSC6,DD_CONF,    INSX2,DD_LEG2,    BOTH,DD_BOTH,
 453:        CONF,DD_CONF,     CONF,DD_CONF,     DELB,DD_BOTH,     DELB,DD_BOTH,
 454:        EDITC1,DD_CONF,   EDIT1,DD_LEG1,    EDIT1,DD_LEG1,    ALL2,DD_ALL,
 455:   
 456:  //    _X   __   __      _X   __   _X      _X   __   X_      _X   __   XX  
 457:  //    _X   _X   __      _X   _X   _X      _X   _X   X_      _X   _X   XX  
 458:  //    _X   X_   __      _X   X_   _X      _X   X_   X_      _X   X_   XX  
 459:  //    _X   XX   __      _X   XX   _X      _X   XX   X_      _X   XX   XX  
 460:   
 461:        CONF,DD_CONF,     INSX1,DD_LEG1,    INSC5,DD_CONF,    BOTH,DD_BOTH,
 462:        INS12,DD_CONF,    INSX12,DD_CONF,   INSY12,DD_CONF,   BOTH,DD_BOTH,
 463:        CONF,DD_CONF,     CONF,DD_CONF,     INSC3,DD_CONF,    INSC3,DD_CONF,
 464:        INSX1,DD_LEG1,    INSX1,DD_LEG1,    INSC1,DD_CONF,    ALL2,DD_ALL,
 465:   
 466:  //    X_   __   __      X_   __   _X      X_   __   X_      X_   __   XX  
 467:  //    X_   _X   __      X_   _X   _X      X_   _X   X_      X_   _X   XX  
 468:  //    X_   X_   __      X_   X_   _X      X_   X_   X_      X_   X_   XX  
 469:  //    X_   XX   __      X_   XX   _X      X_   XX   X_      X_   XX   XX  
 470:   
 471:        CONF,DD_CONF,     DELB,DD_BOTH,     CONF,DD_CONF,     DELB,DD_BOTH,
 472:        CONF,DD_CONF,     INSC4,DD_CONF,    CONF,DD_CONF,     INSC4,DD_CONF,
 473:        DELB,DD_BOTH,     DELB,DD_BOTH,     DELB,DD_BOTH,     DELB,DD_BOTH,
 474:        DEL1,DD_LEG1,     DEL1,DD_LEG1,     DEL1,DD_LEG1,     ALL2,DD_ALL,
 475:   
 476:  //    XX   __   __      XX   __   _X      XX   __   X_      XX   __   XX  
 477:  //    XX   _X   __      XX   _X   _X      XX   _X   X_      XX   _X   XX  
 478:  //    XX   X_   __      XX   X_   _X      XX   X_   X_      XX   X_   XX  
 479:  //    XX   XX   __      XX   XX   _X      XX   XX   X_      XX   XX   XX  
 480:   
 481:        EDITC2,DD_CONF,   EDIT2,DD_LEG2,    EDIT2,DD_LEG2,    ALL1,DD_ALL,
 482:        INSX2,DD_LEG2,    INSC2,DD_CONF,    INSX2,DD_LEG2,    ALL1,DD_ALL,
 483:        DEL2,DD_LEG2,     DEL2,DD_LEG2,     DEL2,DD_LEG2,     ALL1,DD_ALL,
 484:        ALL,DD_ALL,       ALL,DD_ALL,       ALL,DD_ALL,       ALL,DD_ALL
 485:  } ;
 486:   
 487:  DiffDiffs
 488:  DiffMerge::DiffDiff()
 489:  {
 490:          /* All legs completely output? */
 491:   
 492:          if( bf->end == bf->Lines() &&
 493:              lf1->end == lf1->Lines() &&
 494:              lf2->end == lf2->Lines() )
 495:                  return( DD_EOF );
 496:   
 497:          /* Advance to next snake, if past old one. */
 498:   
 499:          df1->AdvanceAndSync();
 500:          df2->AdvanceAndSync();
 501:          df3->AdvanceAndSync();
 502:   
 503:          int lb = 0, l1 = 0, l2 = 0;
 504:   
 505:          DiffGrid d = diffGrid
 506:                  [ df1->StartLeg() == 0 ][ df1->StartBase() == 0 ]
 507:                  [ df2->StartLeg() == 0 ][ df2->StartBase() == 0 ]
 508:                  [ df3->StartL1() == 0 ][ df3->StartL2() == 0 ];
 509:   
 510:          Mode initialMode = d.mode;
 511:   
 512:          // Pre-process rules,  typically the outer snake information
 513:          // contradicts the inner snake, its not perfect, but we use
 514:          // the length of the snake to determine the best outcome.
 515:   
 516:          switch( d.mode )
 517:          {
 518:          case INSC1:
 519:              if( df3->EndL1() > df2->EndBase() )   // trust outer - insert L2
 520:              { d.mode = INSY2; d.diffs = DD_LEG2; break; }
 521:   
 522:              d.mode = INS1; d.diffs = DD_LEG1;     // trust inner - insert L1
 523:              break;
 524:   
 525:          case INSC2:
 526:              if( df3->EndL2() > df1->EndBase() )   // trust outer - insert L2
 527:              { d.mode = INSY1; d.diffs = DD_LEG1; break; }
 528:   
 529:              d.mode = INS2; d.diffs = DD_LEG2;     // trust inner = insert L1
 530:              break;
 531:   
 532:          case INSC3:
 533:              if( df3->EndL1() >= df1->EndLeg() )   // trust outer - delete base
 534:              { d.mode = DELB1; d.diffs = DD_BOTH; break; }
 535:   
 536:              d.mode = INS1; d.diffs = DD_LEG1;     // trust inner - insert L1
 537:              break;
 538:   
 539:          case INSC4:
 540:              if( df3->EndL2() >= df2->EndLeg() )   // trust outer - delete base
 541:              { d.mode = DELB2; d.diffs = DD_BOTH; break; }
 542:   
 543:              d.mode = INS2; d.diffs = DD_LEG2;     // trust inner - insert L2
 544:              break;
 545:   
 546:          case INSC5:
 547:              if( df3->EndL1() >= df1->EndLeg() )   // trust outer - delete base
 548:              { d.mode = DELB1; d.diffs = DD_BOTH; break; }
 549:   
 550:              if( df3->EndL1() >= df1->EndBase() )  // trust outer - insert L2
 551:              { d.mode = INSX2; d.diffs = DD_LEG2; break; }
 552:   
 553:              d.mode = INS1; d.diffs = DD_LEG1;     // trust inner - insert L1
 554:              break;
 555:   
 556:          case INSC6:
 557:              if( df3->EndL2() >= df2->EndLeg() )   // trust outer - delete base
 558:              { d.mode = DELB2; d.diffs = DD_BOTH; break; }
 559:   
 560:              if( df3->EndL2() >= df2->EndBase() )  // trust outer - insert L1
 561:              { d.mode = INSX1; d.diffs = DD_LEG1; break; }
 562:   
 563:              d.mode = INS2; d.diffs = DD_LEG2;     // trust inner - insert L2
 564:              break;
 565:   
 566:          case INSX12:
 567:              if( df3->StartL1() < df1->StartLeg() &&
 568:                  df3->EndL1() >= df1->StartLeg() &&
 569:                  df3->EndL2() >= df2->StartLeg() ) // trust outer - insert L1
 570:              { d.mode = INSY1; d.diffs = DD_LEG1; }
 571:              break;
 572:   
 573:          case INSY12:
 574:              if( df3->StartL2() < df2->StartLeg() &&
 575:                  df3->EndL2() >= df2->StartLeg() &&
 576:                  df3->EndL1() >= df1->StartLeg() ) // trust outer - insert L2
 577:              { d.mode = INSY2; d.diffs = DD_LEG2; }
 578:              break;
 579:          }
 580:   
 581:          // Advance files
 582:   
 583:          switch( d.mode )
 584:          {
 585:          case ALL1:
 586:              lb = l1 = l2 = dmin( df1->EndBase(), df3->EndL1() );
 587:              break;
 588:   
 589:          case ALL2:
 590:              lb = l1 = l2 = dmin( df2->EndBase(), df3->EndL2() );
 591:              break;
 592:   
 593:          case ALL:
 594:              lb = l1 = l2 = dmin( df1->EndBase(), df2->EndBase() );
 595:              break;
 596:   
 597:          case BOTH:
 598:              lb = dmin( df1->StartBase(), df2->StartBase() );
 599:              l1 = dmin( df1->StartLeg(), df3->EndL1() );
 600:              l2 = dmin( df2->StartLeg(), df3->EndL2() );
 601:              l1 = l2 = dmin( l1, l2 );
 602:              break;
 603:   
 604:          case BOTHX:
 605:              lb = dmin( df1->StartBase(), df2->StartBase() );
 606:              l1 = dmin( df1->StartLeg(), df3->EndL1() );
 607:              l2 = dmin( df2->StartLeg(), df3->EndL2() );
 608:              l1 = l2 = dmin( l1, l2 );
 609:              lb = dmin( lb, l1 );
 610:              break;
 611:   
 612:          case DELB:
 613:              lb = dmin( df1->StartBase(), df2->StartBase() );
 614:              break;
 615:   
 616:          case DELB1:
 617:              lb = df1->EndBase();
 618:              break;
 619:   
 620:          case DELB2:
 621:              lb = df2->EndBase();
 622:              break;
 623:   
 624:          case DEL1:
 625:              lb = l2 = dmin( df1->StartBase(), df2->EndBase() );
 626:              break;
 627:   
 628:          case DEL2:
 629:              lb = l1 = dmin( df2->StartBase(), df1->EndBase() );
 630:              break;
 631:   
 632:          case EDIT1:
 633:          case EDITC1:
 634:              l1 = dmin( df1->StartLeg(), df3->StartL1() );
 635:              lb = l2 = dmin( df1->StartBase(), df2->EndBase() );
 636:              break;
 637:   
 638:          case EDIT2:
 639:          case EDITC2:
 640:              l2 = dmin( df2->StartLeg(), df3->StartL2() );
 641:              lb = l1 = dmin( df1->EndBase(), df2->StartBase() );
 642:              break;
 643:   
 644:          case INSX1:
 645:              l1 = dmin( df1->StartLeg(), df3->StartL1() );
 646:              break;
 647:   
 648:          case INSX2:
 649:              l2 = dmin( df2->StartLeg(), df3->StartL2() );
 650:              break;
 651:   
 652:          case INSY1:
 653:              l1 = df3->StartL1();
 654:              break;
 655:   
 656:          case INSY2:
 657:              l2 = df3->StartL2();
 658:              break;
 659:   
 660:          case INS1:
 661:              l1 = df1->StartLeg();
 662:              break;
 663:   
 664:          case INS2:
 665:              l2 = df2->StartLeg();
 666:              break;
 667:   
 668:          case INSX12:
 669:              l1 = dmin( df1->StartLeg(), df3->StartL1() );
 670:              l2 = df2->StartLeg();
 671:              break;
 672:   
 673:          case INSY12:
 674:              l1 = df1->StartLeg();
 675:              l2 = dmin( df2->StartLeg(), df3->StartL2() );
 676:              break;
 677:   
 678:          case CONF:
 679:              lb = dmin( df1->StartBase(), df2->StartBase() );
 680:              // fall through
 681:   
 682:          case INS12:
 683:              l1 = dmin( df1->StartLeg(), df3->StartL1() );
 684:              l2 = dmin( df2->StartLeg(), df3->StartL2() );
 685:              break;
 686:          }
 687:   
 688:          if( DEBUG_MERGE )
 689:          {
 690:              printf("\nMMMM %d+%d (%d-%d,%d-%d) %d+%d (%d-%d,%d-%d) ",
 691:                  lf1->end, bf->end, df1->s.y, df1->s.v, df1->s.x, df1->s.u,
 692:                  lf2->end, bf->end, df2->s.y, df2->s.v, df2->s.x, df2->s.u);
 693:   
 694:              printf("%d+%d (%d-%d,%d-%d) [%c%c %c%c %c%c]\n",
 695:                  lf1->end, lf2->end, df3->s.x, df3->s.u, df3->s.y, df3->s.v,
 696:                  df1->StartLeg() == 0 ? 'X' : '_', 
 697:                  df1->StartBase() == 0 ? 'X' : '_',
 698:                  df2->StartLeg() == 0 ? 'X' : '_',
 699:                  df2->StartBase() == 0 ? 'X' : '_',
 700:                  df3->StartL1() == 0 ? 'X' : '_', 
 701:                  df3->StartL2() == 0 ? 'X' : '_');
 702:   
 703:              printf("MMMM %s%s%s advancing %+d,%+d,%+d\n", 
 704:                  ModeNames[ initialMode ], 
 705:                  d.mode != initialMode ? " -> " : "",
 706:                  d.mode != initialMode ? ModeNames[ d.mode ] : "",
 707:                  lb, l1, l2 );
 708:          }
 709:   
 710:          /* Set output range (start/end).  End is new sync point. */
 711:   
 712:          bf->start = bf->end;
 713:          lf1->start = lf1->end;
 714:          lf2->start = lf2->end;
 715:   
 716:          bf->end += lb;
 717:          lf1->end += l1;
 718:          lf2->end += l2;
 719:   
 720:          /* If an action may be downgraded from a conflict (currently only
 721:           * EDITC1 and EDITC2 fit that category), make sure they actually get
 722:           * through the processing or downgrade it now.
 723:           */
 724:   
 725:          if( bf->end == bf->Lines() &&
 726:              lf1->end == lf1->Lines() &&
 727:              lf2->end == lf2->Lines() )
 728:          {
 729:              if( d.mode == EDITC1 ) d.diffs = DD_LEG1; 
 730:              if( d.mode == EDITC2 ) d.diffs = DD_LEG2; 
 731:          }
 732:   
 733:          /* Handle Conflicts (post processing) 
 734:           * 
 735:           * Sometimes it is necessary to extend the conflict area to produce
 736:           * the best conflict.  Advance all snakes to the conflict end region 
 737:           * and see what happens next.  Depending  on the result,  we may grow 
 738:           * the conflict several times before returning.
 739:           */
 740:   
 741:          DiffGrid n;
 742:          int extendConflict = 0;
 743:   
 744:          while( d.diffs == DD_CONF && ( bf->end != bf->Lines() || 
 745:                                         lf1->end != lf1->Lines() || 
 746:                                         lf2->end != lf2->Lines() ) )
 747:          {
 748:              int ab = 0, a1 = 0, a2 = 0;
 749:   
 750:              // Conflict caused by inserting on L1 and L2 can never extend
 751:              // the base.
 752:   
 753:              int noBaseAdvance = ( d.mode == INS12 
 754:                                 || d.mode == INSX12 
 755:                                 || d.mode == INSY12 );
 756:   
 757:              df1->AdvanceAndSync();
 758:              df2->AdvanceAndSync();
 759:              df3->AdvanceAndSync();
 760:   
 761:              n = diffGrid
 762:                  [ df1->StartLeg() == 0 ][ df1->StartBase() == 0 ]
 763:                  [ df2->StartLeg() == 0 ][ df2->StartBase() == 0 ]
 764:                  [ 0 ][ 0 ];
 765:   
 766:              switch( n.mode )
 767:              {
 768:              case INSX1: case INS1:
 769:                  a1 = df1->StartLeg();
 770:                  break;
 771:              case INSX2: case INS2:
 772:                  a2 = df2->StartLeg();
 773:                  break;
 774:              case DEL1:
 775:                  ab = a2 = dmin( df1->StartBase(), df2->EndBase() );
 776:                  break;
 777:              case DEL2: 
 778:                  ab = a1 = dmin( df2->StartBase(), df1->EndBase() );
 779:                  break;
 780:              case DELB:
 781:                  // Prior to adding EDITC1 and EDITC2,  DELB would not 
 782:                  // extend the conflict area, lets keep it that way.
 783:                  if( d.mode != EDITC1 && d.mode != EDITC2 )
 784:                      break;
 785:              case CONF:
 786:                  ab = dmin( df1->StartBase(), df2->StartBase() );
 787:              case INS12:
 788:                  if( !noBaseAdvance )
 789:                  {
 790:                      a1 = dmin( df1->StartLeg(), df3->StartL1() );
 791:                      a2 = dmin( df2->StartLeg(), df3->StartL2() );
 792:                      break;
 793:                  }
 794:   
 795:                  // INS12,INSX12,INSY12 (conflict) made a bad decision and 
 796:                  // went with an outer snake,  since we are still in the 
 797:                  // inner snake/ make up for that now by advancing to 
 798:                  // inner startlegs.
 799:   
 800:                  if( df1->StartLeg() > df3->EndL1() &&
 801:                      df2->StartLeg() > df3->EndL2() )
 802:                  { 
 803:                      a1 = df1->StartLeg(); 
 804:                      a2 = df2->StartLeg(); 
 805:                  }
 806:                  break;
 807:   
 808:              default:
 809:                  break;
 810:              }
 811:   
 812:              // if EDITC (conflict)  doesn't advance in the deleted leg
 813:              // then this isn't a conflict (fall back to edit).  
 814:   
 815:              if( !extendConflict && d.mode == EDITC1 && !ab && !a2 ) 
 816:                  d.diffs = DD_LEG1; 
 817:   
 818:              if( !extendConflict && d.mode == EDITC2 && !ab && !a1 ) 
 819:                  d.diffs = DD_LEG2; 
 820:   
 821:              // INS12 (conflict) cannot advance base - bail
 822:              
 823:              if( noBaseAdvance && ab ) 
 824:                  break;
 825:   
 826:              // No conflict extension  - bail
 827:   
 828:              if( !ab && !a1 && !a2 )
 829:                  break;
 830:   
 831:              // No longer a conflict - bail
 832:   
 833:              if( d.diffs != DD_CONF )
 834:                  break;
 835:   
 836:              if( DEBUG_MERGE )
 837:                  printf("MMMM %s/%s adjustment %+d,%+d,%+d\n",
 838:                      ModeNames[ d.mode ], ModeNames[ n.mode ], ab, a1, a2 );
 839:   
 840:              bf->end += ab;
 841:              lf1->end += a1;
 842:              lf2->end += a2;
 843:   
 844:              extendConflict++;
 845:          }
 846:   
 847:          return d.diffs;
 848:  }