1// UpdatePair.cpp
2
3#include "StdAfx.h"
4
5#include <time.h>
6// #include <stdio.h>
7
8#include "../../../Common/Wildcard.h"
9
10#include "../../../Windows/TimeUtils.h"
11
12#include "SortUtils.h"
13#include "UpdatePair.h"
14
15using namespace NWindows;
16using namespace NTime;
17
18
19/*
20  a2.Prec =
21  {
22    0 (k_PropVar_TimePrec_0):
23       if GetProperty(kpidMTime) returned 0 and
24          GetProperty(kpidTimeType) did not returned VT_UI4.
25       7z, wim, tar in 7-Zip before v21)
26    in that case we use
27      (prec) that is set by IOutArchive::GetFileTimeType()
28  }
29*/
30
31static int MyCompareTime(unsigned prec, const CFiTime &f1, const CArcTime &a2)
32{
33  // except of precision, we also have limitation, when timestamp is out of range
34
35  /* if (Prec) in archive item is defined, then use global (prec) */
36  if (a2.Prec != k_PropVar_TimePrec_0)
37    prec = a2.Prec;
38
39  CArcTime a1;
40  a1.Set_From_FiTime(f1);
41  /* Set_From_FiTime() must set full form precision:
42     k_PropVar_TimePrec_Base + numDigits
43     windows: 7 digits, non-windows: 9 digits */
44
45  if (prec == k_PropVar_TimePrec_DOS)
46  {
47    const UInt32 dosTime1 = a1.Get_DosTime();
48    const UInt32 dosTime2 = a2.Get_DosTime();
49    return MyCompare(dosTime1, dosTime2);
50  }
51
52  if (prec == k_PropVar_TimePrec_Unix)
53  {
54    const Int64 u2 = FileTime_To_UnixTime64(a2.FT);
55    if (u2 == 0 || u2 == (UInt32)0xFFFFFFFF)
56    {
57      // timestamp probably was saturated in archive to 32-bit
58      // so we use saturated 32-bit value for disk file too.
59      UInt32 u1;
60      FileTime_To_UnixTime(a1.FT, u1);
61      const UInt32 u2_32 = (UInt32)u2;
62      return MyCompare(u1, u2_32);
63    }
64
65    const Int64 u1 = FileTime_To_UnixTime64(a1.FT);
66    return MyCompare(u1, u2);
67    // prec = k_PropVar_TimePrec_Base; // for debug
68  }
69
70  if (prec == k_PropVar_TimePrec_0)
71    prec = k_PropVar_TimePrec_Base + 7;
72  else if (prec == k_PropVar_TimePrec_HighPrec)
73    prec = k_PropVar_TimePrec_Base + 9;
74  else if (prec < k_PropVar_TimePrec_Base)
75    prec = k_PropVar_TimePrec_Base;
76  else if (prec > k_PropVar_TimePrec_Base + 9)
77    prec = k_PropVar_TimePrec_Base + 7;
78
79  // prec now is full form: k_PropVar_TimePrec_Base + numDigits;
80  if (prec > a1.Prec && a1.Prec >= k_PropVar_TimePrec_Base)
81    prec = a1.Prec;
82
83  const unsigned numDigits = prec - k_PropVar_TimePrec_Base;
84  if (numDigits >= 7)
85  {
86    const int comp = CompareFileTime(&a1.FT, &a2.FT);
87    if (comp != 0 || numDigits == 7)
88      return comp;
89    return MyCompare(a1.Ns100, a2.Ns100);
90  }
91  UInt32 d = 1;
92  for (unsigned k = numDigits; k < 7; k++)
93    d *= 10;
94  const UInt64 v1 = a1.Get_FILETIME_as_UInt64() / d * d;
95  const UInt64 v2 = a2.Get_FILETIME_as_UInt64() / d * d;
96  // printf("\ndelta=%d numDigits=%d\n", (unsigned)(v1- v2), numDigits);
97  return MyCompare(v1, v2);
98}
99
100
101
102static const char * const k_Duplicate_inArc_Message = "Duplicate filename in archive:";
103static const char * const k_Duplicate_inDir_Message = "Duplicate filename on disk:";
104static const char * const k_NotCensoredCollision_Message = "Internal file name collision (file on disk, file in archive):";
105
106Z7_ATTR_NORETURN
107static
108void ThrowError(const char *message, const UString &s1, const UString &s2)
109{
110  UString m (message);
111  m.Add_LF(); m += s1;
112  m.Add_LF(); m += s2;
113  throw m;
114}
115
116static int CompareArcItemsBase(const CArcItem &ai1, const CArcItem &ai2)
117{
118  const int res = CompareFileNames(ai1.Name, ai2.Name);
119  if (res != 0)
120    return res;
121  if (ai1.IsDir != ai2.IsDir)
122    return ai1.IsDir ? -1 : 1;
123  return 0;
124}
125
126static int CompareArcItems(const unsigned *p1, const unsigned *p2, void *param)
127{
128  const unsigned i1 = *p1;
129  const unsigned i2 = *p2;
130  const CObjectVector<CArcItem> &arcItems = *(const CObjectVector<CArcItem> *)param;
131  const int res = CompareArcItemsBase(arcItems[i1], arcItems[i2]);
132  if (res != 0)
133    return res;
134  return MyCompare(i1, i2);
135}
136
137void GetUpdatePairInfoList(
138    const CDirItems &dirItems,
139    const CObjectVector<CArcItem> &arcItems,
140    NFileTimeType::EEnum fileTimeType,
141    CRecordVector<CUpdatePair> &updatePairs)
142{
143  CUIntVector dirIndices, arcIndices;
144
145  const unsigned numDirItems = dirItems.Items.Size();
146  const unsigned numArcItems = arcItems.Size();
147
148  CIntArr duplicatedArcItem(numArcItems);
149  {
150    int *vals = &duplicatedArcItem[0];
151    for (unsigned i = 0; i < numArcItems; i++)
152      vals[i] = 0;
153  }
154
155  {
156    arcIndices.ClearAndSetSize(numArcItems);
157    if (numArcItems != 0)
158    {
159      unsigned *vals = &arcIndices[0];
160      for (unsigned i = 0; i < numArcItems; i++)
161        vals[i] = i;
162    }
163    arcIndices.Sort(CompareArcItems, (void *)&arcItems);
164    for (unsigned i = 0; i + 1 < numArcItems; i++)
165      if (CompareArcItemsBase(
166          arcItems[arcIndices[i]],
167          arcItems[arcIndices[i + 1]]) == 0)
168      {
169        duplicatedArcItem[i] = 1;
170        duplicatedArcItem[i + 1] = -1;
171      }
172  }
173
174  UStringVector dirNames;
175  {
176    dirNames.ClearAndReserve(numDirItems);
177    unsigned i;
178    for (i = 0; i < numDirItems; i++)
179      dirNames.AddInReserved(dirItems.GetLogPath(i));
180    SortFileNames(dirNames, dirIndices);
181    for (i = 0; i + 1 < numDirItems; i++)
182    {
183      const UString &s1 = dirNames[dirIndices[i]];
184      const UString &s2 = dirNames[dirIndices[i + 1]];
185      if (CompareFileNames(s1, s2) == 0)
186        ThrowError(k_Duplicate_inDir_Message, s1, s2);
187    }
188  }
189
190  unsigned dirIndex = 0;
191  unsigned arcIndex = 0;
192
193  int prevHostFile = -1;
194  const UString *prevHostName = NULL;
195
196  while (dirIndex < numDirItems || arcIndex < numArcItems)
197  {
198    CUpdatePair pair;
199
200    int dirIndex2 = -1;
201    int arcIndex2 = -1;
202    const CDirItem *di = NULL;
203    const CArcItem *ai = NULL;
204
205    int compareResult = -1;
206    const UString *name = NULL;
207
208    if (dirIndex < numDirItems)
209    {
210      dirIndex2 = (int)dirIndices[dirIndex];
211      di = &dirItems.Items[(unsigned)dirIndex2];
212    }
213
214    if (arcIndex < numArcItems)
215    {
216      arcIndex2 = (int)arcIndices[arcIndex];
217      ai = &arcItems[(unsigned)arcIndex2];
218      compareResult = 1;
219      if (dirIndex < numDirItems)
220      {
221        compareResult = CompareFileNames(dirNames[(unsigned)dirIndex2], ai->Name);
222        if (compareResult == 0)
223        {
224          if (di->IsDir() != ai->IsDir)
225            compareResult = (ai->IsDir ? 1 : -1);
226        }
227      }
228    }
229
230    if (compareResult < 0)
231    {
232      name = &dirNames[(unsigned)dirIndex2];
233      pair.State = NUpdateArchive::NPairState::kOnlyOnDisk;
234      pair.DirIndex = dirIndex2;
235      dirIndex++;
236    }
237    else if (compareResult > 0)
238    {
239      name = &ai->Name;
240      pair.State = ai->Censored ?
241          NUpdateArchive::NPairState::kOnlyInArchive:
242          NUpdateArchive::NPairState::kNotMasked;
243      pair.ArcIndex = arcIndex2;
244      arcIndex++;
245    }
246    else
247    {
248      const int dupl = duplicatedArcItem[arcIndex];
249      if (dupl != 0)
250        ThrowError(k_Duplicate_inArc_Message, ai->Name, arcItems[arcIndices[(unsigned)((int)arcIndex + dupl)]].Name);
251
252      name = &dirNames[(unsigned)dirIndex2];
253      if (!ai->Censored)
254        ThrowError(k_NotCensoredCollision_Message, *name, ai->Name);
255
256      pair.DirIndex = dirIndex2;
257      pair.ArcIndex = arcIndex2;
258
259      int compResult = 0;
260      if (ai->MTime.Def)
261      {
262        compResult = MyCompareTime((unsigned)fileTimeType, di->MTime, ai->MTime);
263      }
264      switch (compResult)
265      {
266        case -1: pair.State = NUpdateArchive::NPairState::kNewInArchive; break;
267        case  1: pair.State = NUpdateArchive::NPairState::kOldInArchive; break;
268        default:
269          pair.State = (ai->Size_Defined && di->Size == ai->Size) ?
270              NUpdateArchive::NPairState::kSameFiles :
271              NUpdateArchive::NPairState::kUnknowNewerFiles;
272      }
273
274      dirIndex++;
275      arcIndex++;
276    }
277
278    if (
279       #ifdef _WIN32
280        (di && di->IsAltStream) ||
281       #endif
282        (ai && ai->IsAltStream))
283    {
284      if (prevHostName)
285      {
286        const unsigned hostLen = prevHostName->Len();
287        if (name->Len() > hostLen)
288          if ((*name)[hostLen] == ':' && CompareFileNames(*prevHostName, name->Left(hostLen)) == 0)
289            pair.HostIndex = prevHostFile;
290      }
291    }
292    else
293    {
294      prevHostFile = (int)updatePairs.Size();
295      prevHostName = name;
296    }
297
298    updatePairs.Add(pair);
299  }
300
301  updatePairs.ReserveDown();
302}
303