1 /*
2  * KDiff3 - Text Diff And Merge Tool
3  *
4  * SPDX-FileCopyrightText: 2002-2011 Joachim Eibl, joachim.eibl at gmx.de
5  * SPDX-FileCopyrightText: 2018-2020 Michael Reeves reeves.87@gmail.com
6  * SPDX-License-Identifier: GPL-2.0-or-later
7 */
8 #ifndef MERGEFILEINFO_H
9 #define MERGEFILEINFO_H
10 
11 #include "DirectoryInfo.h"
12 #include "diff.h"
13 #include "fileaccess.h"
14 
15 #include <QString>
16 
17 //class DirectoryInfo;
18 
19 enum e_MergeOperation
20 {
21     eTitleId,
22     eNoOperation,
23     // Operations in sync mode (with only two directories):
24     eCopyAToB,
25     eCopyBToA,
26     eDeleteA,
27     eDeleteB,
28     eDeleteAB,
29     eMergeToA,
30     eMergeToB,
31     eMergeToAB,
32 
33     // Operations in merge mode (with two or three directories)
34     eCopyAToDest,
35     eCopyBToDest,
36     eCopyCToDest,
37     eDeleteFromDest,
38     eMergeABCToDest,
39     eMergeABToDest,
40     eConflictingFileTypes, // Error
41     eChangedAndDeleted,    // Error
42     eConflictingAges       // Equal age but files are not!
43 };
44 
45 enum e_Age
46 {
47     eNew,
48     eMiddle,
49     eOld,
50     eNotThere,
51     eAgeEnd
52 };
53 
54 enum e_OperationStatus
55 {
56     eOpStatusNone,
57     eOpStatusDone,
58     eOpStatusError,
59     eOpStatusSkipped,
60     eOpStatusNotSaved,
61     eOpStatusInProgress,
62     eOpStatusToDo
63 };
64 
65 class DirectoryMergeWindow;
66 
67 class MergeFileInfos
68 {
69   public:
70     MergeFileInfos();
71     ~MergeFileInfos();
72 
73     Q_REQUIRED_RESULT QString subPath() const;
74     Q_REQUIRED_RESULT QString fileName() const;
75 
isDirA()76     Q_REQUIRED_RESULT bool isDirA() const { return m_pFileInfoA != nullptr ? m_pFileInfoA->isDir() : false; }
isDirB()77     Q_REQUIRED_RESULT bool isDirB() const { return m_pFileInfoB != nullptr ? m_pFileInfoB->isDir() : false; }
isDirC()78     Q_REQUIRED_RESULT bool isDirC() const { return m_pFileInfoC != nullptr ? m_pFileInfoC->isDir() : false; }
hasDir()79     Q_REQUIRED_RESULT bool hasDir() const { return isDirA() || isDirB() || isDirC(); }
80 
isLinkA()81     Q_REQUIRED_RESULT bool isLinkA() const { return m_pFileInfoA != nullptr ? m_pFileInfoA->isSymLink() : false; }
isLinkB()82     Q_REQUIRED_RESULT bool isLinkB() const { return m_pFileInfoB != nullptr ? m_pFileInfoB->isSymLink() : false; }
isLinkC()83     Q_REQUIRED_RESULT bool isLinkC() const { return m_pFileInfoC != nullptr ? m_pFileInfoC->isSymLink() : false; }
hasLink()84     Q_REQUIRED_RESULT bool hasLink() const { return isLinkA() || isLinkB() || isLinkC(); }
85 
existsInA()86     Q_REQUIRED_RESULT bool existsInA() const { return m_pFileInfoA != nullptr; }
existsInB()87     Q_REQUIRED_RESULT bool existsInB() const { return m_pFileInfoB != nullptr; }
existsInC()88     Q_REQUIRED_RESULT bool existsInC() const { return m_pFileInfoC != nullptr; }
89 
90     Q_REQUIRED_RESULT bool conflictingFileTypes() const;
91 
92     void sort(Qt::SortOrder order);
parent()93     Q_REQUIRED_RESULT inline MergeFileInfos* parent() const { return m_pParent; }
setParent(MergeFileInfos * inParent)94     inline void setParent(MergeFileInfos* inParent) { m_pParent = inParent; }
children()95     Q_REQUIRED_RESULT inline const QList<MergeFileInfos*>& children() const { return m_children; }
addChild(MergeFileInfos * child)96     inline void addChild(MergeFileInfos* child) { m_children.push_back(child); }
clear()97     inline void clear() { m_children.clear(); }
98 
getFileInfoA()99     Q_REQUIRED_RESULT FileAccess* getFileInfoA() const { return m_pFileInfoA; }
getFileInfoB()100     Q_REQUIRED_RESULT FileAccess* getFileInfoB() const { return m_pFileInfoB; }
getFileInfoC()101     Q_REQUIRED_RESULT FileAccess* getFileInfoC() const { return m_pFileInfoC; }
102 
setFileInfoA(FileAccess * newInfo)103     void setFileInfoA(FileAccess* newInfo) { m_pFileInfoA = newInfo; }
setFileInfoB(FileAccess * newInfo)104     void setFileInfoB(FileAccess* newInfo) { m_pFileInfoB = newInfo; }
setFileInfoC(FileAccess * newInfo)105     void setFileInfoC(FileAccess* newInfo) { m_pFileInfoC = newInfo; }
106 
107     Q_REQUIRED_RESULT QString fullNameA() const;
108     Q_REQUIRED_RESULT QString fullNameB() const;
109     Q_REQUIRED_RESULT QString fullNameC() const;
110     Q_REQUIRED_RESULT QString fullNameDest() const;
111 
getDirectoryInfo()112     Q_REQUIRED_RESULT static inline QSharedPointer<DirectoryInfo> getDirectoryInfo() { return gDirInfo; }
113 
getDirNameA()114     Q_REQUIRED_RESULT inline QString getDirNameA() const { return getDirectoryInfo()->dirA().prettyAbsPath(); }
getDirNameB()115     Q_REQUIRED_RESULT inline QString getDirNameB() const { return getDirectoryInfo()->dirB().prettyAbsPath(); }
getDirNameC()116     Q_REQUIRED_RESULT inline QString getDirNameC() const { return getDirectoryInfo()->dirC().prettyAbsPath(); }
getDirNameDest()117     Q_REQUIRED_RESULT inline QString getDirNameDest() const { return getDirectoryInfo()->destDir().prettyAbsPath(); }
118 
diffStatus()119     Q_REQUIRED_RESULT inline TotalDiffStatus& diffStatus() { return m_totalDiffStatus; }
120 
getOperation()121     Q_REQUIRED_RESULT inline e_MergeOperation getOperation() const { return m_eMergeOperation; }
setOperation(const e_MergeOperation op)122     inline void setOperation(const e_MergeOperation op) { m_eMergeOperation = op; }
123 
getOpStatus()124     Q_REQUIRED_RESULT inline e_OperationStatus getOpStatus() const { return m_eOpStatus; }
setOpStatus(const e_OperationStatus eOpStatus)125     inline void setOpStatus(const e_OperationStatus eOpStatus) { m_eOpStatus = eOpStatus; }
126 
getAgeA()127     Q_REQUIRED_RESULT inline e_Age getAgeA() const { return m_ageA; }
getAgeB()128     Q_REQUIRED_RESULT inline e_Age getAgeB() const { return m_ageB; }
getAgeC()129     Q_REQUIRED_RESULT inline e_Age getAgeC() const { return m_ageC; }
130 
isEqualAB()131     Q_REQUIRED_RESULT inline bool isEqualAB() const { return m_bEqualAB; }
isEqualAC()132     Q_REQUIRED_RESULT inline bool isEqualAC() const { return m_bEqualAC; }
isEqualBC()133     Q_REQUIRED_RESULT inline bool isEqualBC() const { return m_bEqualBC; }
134     bool compareFilesAndCalcAges(QStringList& errors, QSharedPointer<Options> const &pOptions, DirectoryMergeWindow* pDMW);
135 
136     void updateAge();
137 
138     void updateParents();
139 
140     void updateDirectoryOrLink();
startSimOp()141     inline void startSimOp() { m_bSimOpComplete = false; }
isSimOpRunning()142     Q_REQUIRED_RESULT inline bool isSimOpRunning() const { return !m_bOperationComplete; }
endSimOp()143     inline void endSimOp() { m_bSimOpComplete = true; }
144 
startOperation()145     inline void startOperation() { m_bOperationComplete = false; };
isOperationRunning()146     Q_REQUIRED_RESULT inline bool isOperationRunning() const { return !m_bOperationComplete; }
endOperation()147     inline void endOperation() { m_bOperationComplete = true; };
148 
isThreeWay()149     Q_REQUIRED_RESULT inline bool isThreeWay() const
150     {
151         if(getDirectoryInfo() == nullptr) return false;
152         return getDirectoryInfo()->dirC().isValid();
153     }
existsEveryWhere()154     Q_REQUIRED_RESULT inline bool existsEveryWhere() const { return existsInA() && existsInB() && (existsInC() || !isThreeWay()); }
155 
existsCount()156     Q_REQUIRED_RESULT inline int existsCount() const { return (existsInA() ? 1 : 0) + (existsInB() ? 1 : 0) + (existsInC() ? 1 : 0); }
157 
onlyInA()158     Q_REQUIRED_RESULT inline bool onlyInA() const { return existsInA() && !existsInB() && !existsInC(); }
onlyInB()159     Q_REQUIRED_RESULT inline bool onlyInB() const { return !existsInA() && existsInB() && !existsInC(); }
onlyInC()160     Q_REQUIRED_RESULT inline bool onlyInC() const { return !existsInA() && !existsInB() && existsInC(); }
161 
conflictingAges()162     Q_REQUIRED_RESULT bool conflictingAges() const { return m_bConflictingAges; }
163 
164   private:
165     bool fastFileComparison(FileAccess& fi1, FileAccess& fi2, bool& bError, QString& status, const QSharedPointer<const Options> &pOptions);
setAgeA(const e_Age inAge)166     inline void setAgeA(const e_Age inAge) { m_ageA = inAge; }
setAgeB(const e_Age inAge)167     inline void setAgeB(const e_Age inAge) { m_ageB = inAge; }
setAgeC(const e_Age inAge)168     inline void setAgeC(const e_Age inAge) { m_ageC = inAge; }
169 
170     MergeFileInfos* m_pParent;
171     QList<MergeFileInfos*> m_children;
172 
173     FileAccess* m_pFileInfoA;
174     FileAccess* m_pFileInfoB;
175     FileAccess* m_pFileInfoC;
176 
177     TotalDiffStatus m_totalDiffStatus;
178 
179     e_MergeOperation m_eMergeOperation;
180     e_OperationStatus m_eOpStatus;
181     e_Age m_ageA;
182     e_Age m_ageB;
183     e_Age m_ageC;
184 
185     bool m_bOperationComplete;
186     bool m_bSimOpComplete;
187 
188     bool m_bEqualAB;
189     bool m_bEqualAC;
190     bool m_bEqualBC;
191     bool m_bConflictingAges; // Equal age but files are not!
192 };
193 
194 QTextStream& operator<<(QTextStream& ts, MergeFileInfos& mfi);
195 
196 class MfiCompare
197 {
198     Qt::SortOrder mOrder;
199 
200   public:
MfiCompare(Qt::SortOrder order)201     explicit MfiCompare(Qt::SortOrder order)
202     {
203         mOrder = order;
204     }
operator()205     bool operator()(MergeFileInfos* pMFI1, MergeFileInfos* pMFI2)
206     {
207         bool bDir1 = pMFI1->isDirA() || pMFI1->isDirB() || pMFI1->isDirC();
208         bool bDir2 = pMFI2->isDirA() || pMFI2->isDirB() || pMFI2->isDirC();
209         if(bDir1 == bDir2)
210         {
211             if(mOrder == Qt::AscendingOrder)
212             {
213                 return pMFI1->fileName().compare(pMFI2->fileName(), Qt::CaseInsensitive) < 0;
214             }
215             else
216             {
217                 return pMFI1->fileName().compare(pMFI2->fileName(), Qt::CaseInsensitive) > 0;
218             }
219         }
220         else
221             return bDir1;
222     }
223 };
224 
225 #endif // !MERGEFILEINFO_H
226