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.Add_LF(); m += s1;
50  m.Add_LF(); 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    if (numArcItems != 0)
96    {
97      unsigned *vals = &arcIndices[0];
98      for (unsigned i = 0; i < numArcItems; i++)
99        vals[i] = i;
100    }
101    arcIndices.Sort(CompareArcItems, (void *)&arcItems);
102    for (unsigned i = 0; i + 1 < numArcItems; i++)
103      if (CompareArcItemsBase(
104          arcItems[arcIndices[i]],
105          arcItems[arcIndices[i + 1]]) == 0)
106      {
107        duplicatedArcItem[i] = 1;
108        duplicatedArcItem[i + 1] = -1;
109      }
110  }
111
112  UStringVector dirNames;
113  {
114    dirNames.ClearAndReserve(numDirItems);
115    unsigned i;
116    for (i = 0; i < numDirItems; i++)
117      dirNames.AddInReserved(dirItems.GetLogPath(i));
118    SortFileNames(dirNames, dirIndices);
119    for (i = 0; i + 1 < numDirItems; i++)
120    {
121      const UString &s1 = dirNames[dirIndices[i]];
122      const UString &s2 = dirNames[dirIndices[i + 1]];
123      if (CompareFileNames(s1, s2) == 0)
124        ThrowError(k_Duplicate_inDir_Message, s1, s2);
125    }
126  }
127
128  unsigned dirIndex = 0;
129  unsigned arcIndex = 0;
130
131  int prevHostFile = -1;
132  const UString *prevHostName = NULL;
133
134  while (dirIndex < numDirItems || arcIndex < numArcItems)
135  {
136    CUpdatePair pair;
137
138    int dirIndex2 = -1;
139    int arcIndex2 = -1;
140    const CDirItem *di = NULL;
141    const CArcItem *ai = NULL;
142
143    int compareResult = -1;
144    const UString *name = NULL;
145
146    if (dirIndex < numDirItems)
147    {
148      dirIndex2 = dirIndices[dirIndex];
149      di = &dirItems.Items[dirIndex2];
150    }
151
152    if (arcIndex < numArcItems)
153    {
154      arcIndex2 = arcIndices[arcIndex];
155      ai = &arcItems[arcIndex2];
156      compareResult = 1;
157      if (dirIndex < numDirItems)
158      {
159        compareResult = CompareFileNames(dirNames[dirIndex2], ai->Name);
160        if (compareResult == 0)
161        {
162          if (di->IsDir() != ai->IsDir)
163            compareResult = (ai->IsDir ? 1 : -1);
164        }
165      }
166    }
167
168    if (compareResult < 0)
169    {
170      name = &dirNames[dirIndex2];
171      pair.State = NUpdateArchive::NPairState::kOnlyOnDisk;
172      pair.DirIndex = dirIndex2;
173      dirIndex++;
174    }
175    else if (compareResult > 0)
176    {
177      name = &ai->Name;
178      pair.State = ai->Censored ?
179          NUpdateArchive::NPairState::kOnlyInArchive:
180          NUpdateArchive::NPairState::kNotMasked;
181      pair.ArcIndex = arcIndex2;
182      arcIndex++;
183    }
184    else
185    {
186      int dupl = duplicatedArcItem[arcIndex];
187      if (dupl != 0)
188        ThrowError(k_Duplicate_inArc_Message, ai->Name, arcItems[arcIndices[arcIndex + dupl]].Name);
189
190      name = &dirNames[dirIndex2];
191      if (!ai->Censored)
192        ThrowError(k_NotCensoredCollision_Message, *name, ai->Name);
193
194      pair.DirIndex = dirIndex2;
195      pair.ArcIndex = arcIndex2;
196
197      switch (ai->MTimeDefined ? MyCompareTime(
198          ai->TimeType != - 1 ? (NFileTimeType::EEnum)ai->TimeType : fileTimeType,
199          di->MTime, ai->MTime): 0)
200      {
201        case -1: pair.State = NUpdateArchive::NPairState::kNewInArchive; break;
202        case  1: pair.State = NUpdateArchive::NPairState::kOldInArchive; break;
203        default:
204          pair.State = (ai->SizeDefined && di->Size == ai->Size) ?
205              NUpdateArchive::NPairState::kSameFiles :
206              NUpdateArchive::NPairState::kUnknowNewerFiles;
207      }
208
209      dirIndex++;
210      arcIndex++;
211    }
212
213    if ((di && di->IsAltStream) ||
214        (ai && ai->IsAltStream))
215    {
216      if (prevHostName)
217      {
218        unsigned hostLen = prevHostName->Len();
219        if (name->Len() > hostLen)
220          if ((*name)[hostLen] == ':' && CompareFileNames(*prevHostName, name->Left(hostLen)) == 0)
221            pair.HostIndex = prevHostFile;
222      }
223    }
224    else
225    {
226      prevHostFile = updatePairs.Size();
227      prevHostName = name;
228    }
229
230    updatePairs.Add(pair);
231  }
232
233  updatePairs.ReserveDown();
234}
235