1// UpdatePair.cpp
2
3#include "StdAfx.h"
4
5#include <time.h>
6
7#include "../../../Common/Wildcard.h"
8
9#include "../../../Windows/TimeUtils.h"
10
11#include "SortUtils.h"
12#include "UpdatePair.h"
13
14using namespace NWindows;
15using namespace NTime;
16
17static int MyCompareTime(NFileTimeType::EEnum fileTimeType, const FILETIME &time1, const FILETIME &time2)
18{
19  switch (fileTimeType)
20  {
21    case NFileTimeType::kWindows:
22      return ::CompareFileTime(&time1, &time2);
23    case NFileTimeType::kUnix:
24      {
25        UInt32 unixTime1, unixTime2;
26        FileTimeToUnixTime(time1, unixTime1);
27        FileTimeToUnixTime(time2, unixTime2);
28        return MyCompare(unixTime1, unixTime2);
29      }
30    case NFileTimeType::kDOS:
31      {
32        UInt32 dosTime1, dosTime2;
33        FileTimeToDosTime(time1, dosTime1);
34        FileTimeToDosTime(time2, dosTime2);
35        return MyCompare(dosTime1, dosTime2);
36      }
37  }
38  throw 4191618;
39}
40
41static const char *k_Duplicate_inArc_Message = "Duplicate filename in archive:";
42static const char *k_Duplicate_inDir_Message = "Duplicate filename on disk:";
43static const char *k_NotCensoredCollision_Message = "Internal file name collision (file on disk, file in archive):";
44
45static void ThrowError(const char *message, const UString &s1, const UString &s2)
46{
47  UString m;
48  m.SetFromAscii(message);
49  m += L'\n'; m += s1;
50  m += L'\n'; m += s2;
51  throw m;
52}
53
54static int CompareArcItemsBase(const CArcItem &ai1, const CArcItem &ai2)
55{
56  int res = CompareFileNames(ai1.Name, ai2.Name);
57  if (res != 0)
58    return res;
59  if (ai1.IsDir != ai2.IsDir)
60    return ai1.IsDir ? -1 : 1;
61  return 0;
62}
63
64static int CompareArcItems(const unsigned *p1, const unsigned *p2, void *param)
65{
66  unsigned i1 = *p1;
67  unsigned i2 = *p2;
68  const CObjectVector<CArcItem> &arcItems = *(const CObjectVector<CArcItem> *)param;
69  int res = CompareArcItemsBase(arcItems[i1], arcItems[i2]);
70  if (res != 0)
71    return res;
72  return MyCompare(i1, i2);
73}
74
75void GetUpdatePairInfoList(
76    const CDirItems &dirItems,
77    const CObjectVector<CArcItem> &arcItems,
78    NFileTimeType::EEnum fileTimeType,
79    CRecordVector<CUpdatePair> &updatePairs)
80{
81  CUIntVector dirIndices, arcIndices;
82
83  unsigned numDirItems = dirItems.Items.Size();
84  unsigned numArcItems = arcItems.Size();
85
86  CIntArr duplicatedArcItem(numArcItems);
87  {
88    int *vals = &duplicatedArcItem[0];
89    for (unsigned i = 0; i < numArcItems; i++)
90      vals[i] = 0;
91  }
92
93  {
94    arcIndices.ClearAndSetSize(numArcItems);
95    {
96      unsigned *vals = &arcIndices[0];
97      for (unsigned i = 0; i < numArcItems; i++)
98        vals[i] = i;
99    }
100    arcIndices.Sort(CompareArcItems, (void *)&arcItems);
101    for (unsigned i = 0; i + 1 < numArcItems; i++)
102      if (CompareArcItemsBase(
103          arcItems[arcIndices[i]],
104          arcItems[arcIndices[i + 1]]) == 0)
105      {
106        duplicatedArcItem[i] = 1;
107        duplicatedArcItem[i + 1] = -1;
108      }
109  }
110
111  UStringVector dirNames;
112  {
113    dirNames.ClearAndReserve(numDirItems);
114    unsigned i;
115    for (i = 0; i < numDirItems; i++)
116      dirNames.AddInReserved(dirItems.GetLogPath(i));
117    SortFileNames(dirNames, dirIndices);
118    for (i = 0; i + 1 < numDirItems; i++)
119    {
120      const UString &s1 = dirNames[dirIndices[i]];
121      const UString &s2 = dirNames[dirIndices[i + 1]];
122      if (CompareFileNames(s1, s2) == 0)
123        ThrowError(k_Duplicate_inDir_Message, s1, s2);
124    }
125  }
126
127  unsigned dirIndex = 0;
128  unsigned arcIndex = 0;
129
130  int prevHostFile = -1;
131  const UString *prevHostName = NULL;
132
133  while (dirIndex < numDirItems || arcIndex < numArcItems)
134  {
135    CUpdatePair pair;
136
137    int dirIndex2 = -1;
138    int arcIndex2 = -1;
139    const CDirItem *di = NULL;
140    const CArcItem *ai = NULL;
141
142    int compareResult = -1;
143    const UString *name = NULL;
144
145    if (dirIndex < numDirItems)
146    {
147      dirIndex2 = dirIndices[dirIndex];
148      di = &dirItems.Items[dirIndex2];
149    }
150
151    if (arcIndex < numArcItems)
152    {
153      arcIndex2 = arcIndices[arcIndex];
154      ai = &arcItems[arcIndex2];
155      compareResult = 1;
156      if (dirIndex < numDirItems)
157      {
158        compareResult = CompareFileNames(dirNames[dirIndex2], ai->Name);
159        if (compareResult == 0)
160        {
161          if (di->IsDir() != ai->IsDir)
162            compareResult = (ai->IsDir ? 1 : -1);
163        }
164      }
165    }
166
167    if (compareResult < 0)
168    {
169      name = &dirNames[dirIndex2];
170      pair.State = NUpdateArchive::NPairState::kOnlyOnDisk;
171      pair.DirIndex = dirIndex2;
172      dirIndex++;
173    }
174    else if (compareResult > 0)
175    {
176      name = &ai->Name;
177      pair.State = ai->Censored ?
178          NUpdateArchive::NPairState::kOnlyInArchive:
179          NUpdateArchive::NPairState::kNotMasked;
180      pair.ArcIndex = arcIndex2;
181      arcIndex++;
182    }
183    else
184    {
185      int dupl = duplicatedArcItem[arcIndex];
186      if (dupl != 0)
187        ThrowError(k_Duplicate_inArc_Message, ai->Name, arcItems[arcIndices[arcIndex + dupl]].Name);
188
189      name = &dirNames[dirIndex2];
190      if (!ai->Censored)
191        ThrowError(k_NotCensoredCollision_Message, *name, ai->Name);
192
193      pair.DirIndex = dirIndex2;
194      pair.ArcIndex = arcIndex2;
195
196      switch (ai->MTimeDefined ? MyCompareTime(
197          ai->TimeType != - 1 ? (NFileTimeType::EEnum)ai->TimeType : fileTimeType,
198          di->MTime, ai->MTime): 0)
199      {
200        case -1: pair.State = NUpdateArchive::NPairState::kNewInArchive; break;
201        case  1: pair.State = NUpdateArchive::NPairState::kOldInArchive; break;
202        default:
203          pair.State = (ai->SizeDefined && di->Size == ai->Size) ?
204              NUpdateArchive::NPairState::kSameFiles :
205              NUpdateArchive::NPairState::kUnknowNewerFiles;
206      }
207
208      dirIndex++;
209      arcIndex++;
210    }
211
212    if ((di && di->IsAltStream) ||
213        (ai && ai->IsAltStream))
214    {
215      if (prevHostName)
216      {
217        unsigned hostLen = prevHostName->Len();
218        if (name->Len() > hostLen)
219          if ((*name)[hostLen] == ':' && CompareFileNames(*prevHostName, name->Left(hostLen)) == 0)
220            pair.HostIndex = prevHostFile;
221      }
222    }
223    else
224    {
225      prevHostFile = updatePairs.Size();
226      prevHostName = name;
227    }
228
229    updatePairs.Add(pair);
230  }
231
232  updatePairs.ReserveDown();
233}
234