/*-1* Copyright (c) 2014 Baptiste Daroussin <[email protected]>2* All rights reserved.3*~4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer9* in this position and unchanged.10* 2. Redistributions in binary form must reproduce the above copyright11* notice, this list of conditions and the following disclaimer in the12* documentation and/or other materials provided with the distribution.13*~14* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR15* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES16* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.17* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,18* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT19* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,20* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY21* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT22* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF23* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.24*/2526/*27* This code has been extracted from the fossil scm code28* and modified29*/3031/*32** Copyright (c) 2007 D. Richard Hipp33**34** This program is free software; you can redistribute it and/or35** modify it under the terms of the Simplified BSD License (also36** known as the "2-Clause License" or "FreeBSD License".)3738** This program is distributed in the hope that it will be useful,39** but without any warranty; without even the implied warranty of40** merchantability or fitness for a particular purpose.41**42** Author contact information:43** [email protected]44** http://www.hwaci.com/drh/45**46*******************************************************************************47**48** This module implements a 3-way merge49*/5051#include <sys/types.h>52#include <xstring.h>5354#include <string.h>55#include <stdlib.h>56#include <stdio.h>5758#include "private/utils.h"5960/* The minimum of two integers */61#ifndef min62# define min(A,B) (A<B?A:B)63#endif6465/*66** Compare N lines of text from pV1 and pV2. If the lines67** are the same, return true. Return false if one or more of the N68** lines are different.69**70** The cursors on both pV1 and pV2 is unchanged by this comparison.71*/72static int sameLines(const char *pV1, const char *pV2, int N){73int i;74char c;7576if( N==0 ) return 1;77for(i=0; (c=pV1[i])==pV2[i]; i++){78if( c=='\n' || c==0 ){79N--;80if( N==0 || c==0 ) return 1;81}82}83return 0;84}8586/*87** Look at the next edit triple in both aC1 and aC2. (An "edit triple" is88** three integers describing the number of copies, deletes, and inserts in89** moving from the original to the edited copy of the file.) If the three90** integers of the edit triples describe an identical edit, then return 1.91** If the edits are different, return 0.92*/93static int sameEdit(94int *aC1, /* Array of edit integers for file 1 */95int *aC2, /* Array of edit integers for file 2 */96const char *pV1, /* Text of file 1 */97const char *pV2 /* Text of file 2 */98){99if( aC1[0]!=aC2[0] ) return 0;100if( aC1[1]!=aC2[1] ) return 0;101if( aC1[2]!=aC2[2] ) return 0;102if( sameLines(pV1, pV2, aC1[2]) ) return 1;103return 0;104}105106/*107** Copy N lines of text from from into to.108** The return value is the number of characters copied, normally including109** the \n and should be used to advance the 'from' pointer.110**111** If to==NULL then this routine simply counts characters for N lines.112*/113114static int115buf_copy_lines(xstring *to, const char *from, int N)116{117int cnt = 0;118int i;119120if (N == 0)121return (0);122i = 0;123while (from[i] != 0) {124if (from[i] == '\n') {125cnt++;126if (cnt == N) {127i++;128break;129}130}131i++;132}133if (to)134fwrite(from, i, 1, to->fp);135return (i);136}137138/*139** Do a three-way merge. Initialize pOut to contain the result.140**141** The merge is an edit against pV2. Both pV1 and pV2 have a142** common origin at pPivot. Apply the changes of pPivot ==> pV1143** to pV2.144**145** The return is 0 upon complete success. If any input file is binary,146** -1 is returned and pOut is unmodified. If there are merge147** conflicts, the merge proceeds as best as it can and the number148** of conflicts is returns149*/150static int151buf_merge(char *pPivot, char *pV1, char *pV2, xstring *pOut){152int *aC1; /* Changes from pPivot to pV1 */153int *aC2; /* Changes from pPivot to pV2 */154int i1, i2; /* Index into aC1[] and aC2[] */155int nCpy, nDel, nIns; /* Number of lines to copy, delete, or insert */156int limit1, limit2; /* Sizes of aC1[] and aC2[] */157158xstring_reset(pOut); /* Merge results stored in pOut */159160/* Compute the edits that occur from pPivot => pV1 (into aC1)161** and pPivot => pV2 (into aC2). Each of the aC1 and aC2 arrays is162** an array of integer triples. Within each triple, the first integer163** is the number of lines of text to copy directly from the pivot,164** the second integer is the number of lines of text to omit from the165** pivot, and the third integer is the number of lines of text that are166** inserted. The edit array ends with a triple of 0,0,0.167*/168aC1 = text_diff(pPivot, pV1);169aC2 = text_diff(pPivot, pV2);170if( aC1==0 || aC2==0 ){171free(aC1);172free(aC2);173return -1;174}175176/* Determine the length of the aC1[] and aC2[] change vectors */177for(i1=0; aC1[i1] || aC1[i1+1] || aC1[i1+2]; i1+=3){}178limit1 = i1;179for(i2=0; aC2[i2] || aC2[i2+1] || aC2[i2+2]; i2+=3){}180limit2 = i2;181182/* Loop over the two edit vectors and use them to compute merged text183** which is written into pOut. i1 and i2 are multiples of 3 which are184** indices into aC1[] and aC2[] to the edit triple currently being185** processed186*/187i1 = i2 = 0;188while( i1<limit1 && i2<limit2 ){189190if( aC1[i1]>0 && aC2[i2]>0 ){191/* Output text that is unchanged in both V1 and V2 */192nCpy = min(aC1[i1], aC2[i2]);193pPivot += buf_copy_lines(pOut, pPivot, nCpy);194pV1 += buf_copy_lines(NULL, pV1, nCpy);195pV2 += buf_copy_lines(NULL, pV2, nCpy);196aC1[i1] -= nCpy;197aC2[i2] -= nCpy;198}else199if( aC1[i1] >= aC2[i2+1] && aC1[i1]>0 && aC2[i2+1]+aC2[i2+2]>0 ){200/* Output edits to V2 that occurs within unchanged regions of V1 */201nDel = aC2[i2+1];202nIns = aC2[i2+2];203pPivot += buf_copy_lines(NULL, pPivot, nDel);204pV1 += buf_copy_lines(NULL, pV1, nDel);205pV2 += buf_copy_lines(pOut, pV2, nIns);206aC1[i1] -= nDel;207i2 += 3;208}else209if( aC2[i2] >= aC1[i1+1] && aC2[i2]>0 && aC1[i1+1]+aC1[i1+2]>0 ){210/* Output edits to V1 that occur within unchanged regions of V2 */211nDel = aC1[i1+1];212nIns = aC1[i1+2];213pPivot += buf_copy_lines(NULL, pPivot, nDel);214pV2 += buf_copy_lines(NULL, pV2, nDel);215pV1 += buf_copy_lines(pOut, pV1, nIns);216aC2[i2] -= nDel;217i1 += 3;218}else219if( sameEdit(&aC1[i1], &aC2[i2], pV1, pV2) ){220/* Output edits that are identical in both V1 and V2. */221nDel = aC1[i1+1];222nIns = aC1[i1+2];223pPivot += buf_copy_lines(NULL, pPivot, nDel);224pV1 += buf_copy_lines(pOut, pV1, nIns);225pV2 += buf_copy_lines(NULL, pV2, nIns);226i1 += 3;227i2 += 3;228}else229{230return (-1);231}232233/* If we are finished with an edit triple, advance to the next234** triple.235*/236if( i1<limit1 && aC1[i1]==0 && aC1[i1+1]==0 && aC1[i1+2]==0 ) i1+=3;237if( i2<limit2 && aC2[i2]==0 && aC2[i2+1]==0 && aC2[i2+2]==0 ) i2+=3;238}239240/* When one of the two edit vectors reaches its end, there might still241** be an insert in the other edit vector. Output this remaining242** insert.243*/244if( i1<limit1 && aC1[i1+2]>0 ){245buf_copy_lines(pOut, pV1, aC1[i1+2]);246}else if( i2<limit2 && aC2[i2+2]>0 ){247buf_copy_lines(pOut, pV2, aC2[i2+2]);248}249250free(aC1);251free(aC2);252return 0;253}254255/*256** This routine is a wrapper around blob_merge() with the following257** enhancements:258**259** (1) If the merge-command is defined, then use the external merging260** program specified instead of the built-in blob-merge to do the261** merging. Panic if the external merger fails.262** ** Not currently implemented **263**264** (2) If gmerge-command is defined and there are merge conflicts in265** blob_merge() then invoke the external graphical merger to resolve266** the conflicts.267**268** (3) If a merge conflict occurs and gmerge-command is not defined,269** then write the pivot, original, and merge-in files to the270** filesystem.271*/272int merge_3way(273char *pPivot, /* Common ancestor (older) */274char *pV1, /* Name of file for version merging into (mine) */275char *pV2, /* Version merging from (yours) */276xstring *pOut /* Output written here */277){278int rc; /* Return code of subroutines and this routine */279280rc = buf_merge(pPivot, pV1, pV2, pOut);281return rc;282}283284285