1// Update.cpp
2
3#include "StdAfx.h"
4
5#include "Update.h"
6
7#include "../../../Common/IntToString.h"
8#include "../../../Common/StringConvert.h"
9
10#include "../../../Windows/DLL.h"
11#include "../../../Windows/FileDir.h"
12#include "../../../Windows/FileFind.h"
13#include "../../../Windows/FileName.h"
14#include "../../../Windows/PropVariant.h"
15#include "../../../Windows/PropVariantConv.h"
16#include "../../../Windows/TimeUtils.h"
17
18#include "../../Common/FileStreams.h"
19#include "../../Common/LimitedStreams.h"
20
21#include "../../Compress/CopyCoder.h"
22
23#include "../Common/DirItem.h"
24#include "../Common/EnumDirItems.h"
25#include "../Common/OpenArchive.h"
26#include "../Common/UpdateProduce.h"
27
28#include "EnumDirItems.h"
29#include "SetProperties.h"
30#include "TempFiles.h"
31#include "UpdateCallback.h"
32
33static const char *kUpdateIsNotSupoorted =
34  "update operations are not supported for this archive";
35
36using namespace NWindows;
37using namespace NCOM;
38using namespace NFile;
39using namespace NDir;
40using namespace NName;
41
42static CFSTR kTempFolderPrefix = FTEXT("7zE");
43
44
45static bool DeleteEmptyFolderAndEmptySubFolders(const FString &path)
46{
47  NFind::CFileInfo fileInfo;
48  FString pathPrefix = path + FCHAR_PATH_SEPARATOR;
49  {
50    NFind::CEnumerator enumerator(pathPrefix + FCHAR_ANY_MASK);
51    while (enumerator.Next(fileInfo))
52    {
53      if (fileInfo.IsDir())
54        if (!DeleteEmptyFolderAndEmptySubFolders(pathPrefix + fileInfo.Name))
55          return false;
56    }
57  }
58  /*
59  // we don't need clear read-only for folders
60  if (!MySetFileAttributes(path, 0))
61    return false;
62  */
63  return RemoveDir(path);
64}
65
66
67using namespace NUpdateArchive;
68
69class COutMultiVolStream:
70  public IOutStream,
71  public CMyUnknownImp
72{
73  unsigned _streamIndex; // required stream
74  UInt64 _offsetPos; // offset from start of _streamIndex index
75  UInt64 _absPos;
76  UInt64 _length;
77
78  struct CAltStreamInfo
79  {
80    COutFileStream *StreamSpec;
81    CMyComPtr<IOutStream> Stream;
82    FString Name;
83    UInt64 Pos;
84    UInt64 RealSize;
85  };
86  CObjectVector<CAltStreamInfo> Streams;
87public:
88  // CMyComPtr<IArchiveUpdateCallback2> VolumeCallback;
89  CRecordVector<UInt64> Sizes;
90  FString Prefix;
91  CTempFiles *TempFiles;
92
93  void Init()
94  {
95    _streamIndex = 0;
96    _offsetPos = 0;
97    _absPos = 0;
98    _length = 0;
99  }
100
101  bool SetMTime(const FILETIME *mTime);
102  HRESULT Close();
103
104  MY_UNKNOWN_IMP1(IOutStream)
105
106  STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
107  STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition);
108  STDMETHOD(SetSize)(UInt64 newSize);
109};
110
111// static NSynchronization::CCriticalSection g_TempPathsCS;
112
113HRESULT COutMultiVolStream::Close()
114{
115  HRESULT res = S_OK;
116  FOR_VECTOR (i, Streams)
117  {
118    COutFileStream *s = Streams[i].StreamSpec;
119    if (s)
120    {
121      HRESULT res2 = s->Close();
122      if (res2 != S_OK)
123        res = res2;
124    }
125  }
126  return res;
127}
128
129bool COutMultiVolStream::SetMTime(const FILETIME *mTime)
130{
131  bool res = true;
132  FOR_VECTOR (i, Streams)
133  {
134    COutFileStream *s = Streams[i].StreamSpec;
135    if (s)
136      if (!s->SetMTime(mTime))
137        res = false;
138  }
139  return res;
140}
141
142STDMETHODIMP COutMultiVolStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
143{
144  if (processedSize != NULL)
145    *processedSize = 0;
146  while (size > 0)
147  {
148    if (_streamIndex >= Streams.Size())
149    {
150      CAltStreamInfo altStream;
151
152      FChar temp[16];
153      ConvertUInt32ToString(_streamIndex + 1, temp);
154      FString res = temp;
155      while (res.Len() < 3)
156        res = FString(FTEXT('0')) + res;
157      FString name = Prefix + res;
158      altStream.StreamSpec = new COutFileStream;
159      altStream.Stream = altStream.StreamSpec;
160      if (!altStream.StreamSpec->Create(name, false))
161        return ::GetLastError();
162      {
163        // NSynchronization::CCriticalSectionLock lock(g_TempPathsCS);
164        TempFiles->Paths.Add(name);
165      }
166
167      altStream.Pos = 0;
168      altStream.RealSize = 0;
169      altStream.Name = name;
170      Streams.Add(altStream);
171      continue;
172    }
173    CAltStreamInfo &altStream = Streams[_streamIndex];
174
175    unsigned index = _streamIndex;
176    if (index >= Sizes.Size())
177      index = Sizes.Size() - 1;
178    UInt64 volSize = Sizes[index];
179
180    if (_offsetPos >= volSize)
181    {
182      _offsetPos -= volSize;
183      _streamIndex++;
184      continue;
185    }
186    if (_offsetPos != altStream.Pos)
187    {
188      // CMyComPtr<IOutStream> outStream;
189      // RINOK(altStream.Stream.QueryInterface(IID_IOutStream, &outStream));
190      RINOK(altStream.Stream->Seek(_offsetPos, STREAM_SEEK_SET, NULL));
191      altStream.Pos = _offsetPos;
192    }
193
194    UInt32 curSize = (UInt32)MyMin((UInt64)size, volSize - altStream.Pos);
195    UInt32 realProcessed;
196    RINOK(altStream.Stream->Write(data, curSize, &realProcessed));
197    data = (void *)((Byte *)data + realProcessed);
198    size -= realProcessed;
199    altStream.Pos += realProcessed;
200    _offsetPos += realProcessed;
201    _absPos += realProcessed;
202    if (_absPos > _length)
203      _length = _absPos;
204    if (_offsetPos > altStream.RealSize)
205      altStream.RealSize = _offsetPos;
206    if (processedSize != NULL)
207      *processedSize += realProcessed;
208    if (altStream.Pos == volSize)
209    {
210      _streamIndex++;
211      _offsetPos = 0;
212    }
213    if (realProcessed == 0 && curSize != 0)
214      return E_FAIL;
215    break;
216  }
217  return S_OK;
218}
219
220STDMETHODIMP COutMultiVolStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)
221{
222  if (seekOrigin >= 3)
223    return STG_E_INVALIDFUNCTION;
224  switch (seekOrigin)
225  {
226    case STREAM_SEEK_SET: _absPos = offset; break;
227    case STREAM_SEEK_CUR: _absPos += offset; break;
228    case STREAM_SEEK_END: _absPos = _length + offset; break;
229  }
230  _offsetPos = _absPos;
231  if (newPosition != NULL)
232    *newPosition = _absPos;
233  _streamIndex = 0;
234  return S_OK;
235}
236
237STDMETHODIMP COutMultiVolStream::SetSize(UInt64 newSize)
238{
239  if (newSize < 0)
240    return E_INVALIDARG;
241  unsigned i = 0;
242  while (i < Streams.Size())
243  {
244    CAltStreamInfo &altStream = Streams[i++];
245    if ((UInt64)newSize < altStream.RealSize)
246    {
247      RINOK(altStream.Stream->SetSize(newSize));
248      altStream.RealSize = newSize;
249      break;
250    }
251    newSize -= altStream.RealSize;
252  }
253  while (i < Streams.Size())
254  {
255    {
256      CAltStreamInfo &altStream = Streams.Back();
257      altStream.Stream.Release();
258      DeleteFileAlways(altStream.Name);
259    }
260    Streams.DeleteBack();
261  }
262  _offsetPos = _absPos;
263  _streamIndex = 0;
264  _length = newSize;
265  return S_OK;
266}
267
268void CArchivePath::ParseFromPath(const UString &path, EArcNameMode mode)
269{
270  OriginalPath = path;
271
272  SplitPathToParts_2(path, Prefix, Name);
273
274  if (mode == k_ArcNameMode_Add)
275    return;
276  if (mode == k_ArcNameMode_Exact)
277  {
278    BaseExtension.Empty();
279    return;
280  }
281
282  int dotPos = Name.ReverseFind(L'.');
283  if (dotPos < 0)
284    return;
285  if ((unsigned)dotPos == Name.Len() - 1)
286  {
287    Name.DeleteBack();
288    BaseExtension.Empty();
289    return;
290  }
291  const UString ext = Name.Ptr(dotPos + 1);
292  if (BaseExtension.IsEqualToNoCase(ext))
293  {
294    BaseExtension = ext;
295    Name.DeleteFrom(dotPos);
296  }
297  else
298    BaseExtension.Empty();
299}
300
301UString CArchivePath::GetFinalPath() const
302{
303  UString path = GetPathWithoutExt();
304  if (!BaseExtension.IsEmpty())
305    path += UString(L'.') + BaseExtension;
306  return path;
307}
308
309UString CArchivePath::GetFinalVolPath() const
310{
311  UString path = GetPathWithoutExt();
312  if (!BaseExtension.IsEmpty())
313    path += UString(L'.') + VolExtension;
314  return path;
315}
316
317FString CArchivePath::GetTempPath() const
318{
319  FString path = TempPrefix + us2fs(Name);
320  if (!BaseExtension.IsEmpty())
321    path += FString(FTEXT('.')) + us2fs(BaseExtension);
322  path += FTEXT(".tmp");
323  path += TempPostfix;
324  return path;
325}
326
327static const wchar_t *kDefaultArcType = L"7z";
328static const wchar_t *kDefaultArcExt = L"7z";
329static const wchar_t *kSFXExtension =
330  #ifdef _WIN32
331    L"exe";
332  #else
333    L"";
334  #endif
335
336bool CUpdateOptions::InitFormatIndex(const CCodecs *codecs,
337    const CObjectVector<COpenType> &types, const UString &arcPath)
338{
339  if (types.Size() > 1)
340    return false;
341  // int arcTypeIndex = -1;
342  if (types.Size() != 0)
343  {
344    MethodMode.Type = types[0];
345    MethodMode.Type_Defined = true;
346  }
347  if (MethodMode.Type.FormatIndex < 0)
348  {
349    // MethodMode.Type = -1;
350    MethodMode.Type = COpenType();
351    if (ArcNameMode != k_ArcNameMode_Add)
352    {
353      MethodMode.Type.FormatIndex = codecs->FindFormatForArchiveName(arcPath);
354      if (MethodMode.Type.FormatIndex >= 0)
355        MethodMode.Type_Defined = true;
356    }
357  }
358  return true;
359}
360
361bool CUpdateOptions::SetArcPath(const CCodecs *codecs, const UString &arcPath)
362{
363  UString typeExt;
364  int formatIndex = MethodMode.Type.FormatIndex;
365  if (formatIndex < 0)
366  {
367    typeExt = kDefaultArcExt;
368  }
369  else
370  {
371    const CArcInfoEx &arcInfo = codecs->Formats[formatIndex];
372    if (!arcInfo.UpdateEnabled)
373      return false;
374    typeExt = arcInfo.GetMainExt();
375  }
376  UString ext = typeExt;
377  if (SfxMode)
378    ext = kSFXExtension;
379  ArchivePath.BaseExtension = ext;
380  ArchivePath.VolExtension = typeExt;
381  ArchivePath.ParseFromPath(arcPath, ArcNameMode);
382  FOR_VECTOR (i, Commands)
383  {
384    CUpdateArchiveCommand &uc = Commands[i];
385    uc.ArchivePath.BaseExtension = ext;
386    uc.ArchivePath.VolExtension = typeExt;
387    uc.ArchivePath.ParseFromPath(uc.UserArchivePath, ArcNameMode);
388  }
389  return true;
390}
391
392/*
393struct CUpdateProduceCallbackImp: public IUpdateProduceCallback
394{
395  const CObjectVector<CArcItem> *_arcItems;
396  IUpdateCallbackUI *_callback;
397
398  CUpdateProduceCallbackImp(const CObjectVector<CArcItem> *a,
399      IUpdateCallbackUI *callback): _arcItems(a), _callback(callback) {}
400  virtual HRESULT ShowDeleteFile(int arcIndex);
401};
402
403HRESULT CUpdateProduceCallbackImp::ShowDeleteFile(int arcIndex)
404{
405  return _callback->ShowDeleteFile((*_arcItems)[arcIndex].Name);
406}
407*/
408
409bool CRenamePair::Prepare()
410{
411  if (RecursedType != NRecursedType::kNonRecursed)
412    return false;
413  if (!WildcardParsing)
414    return true;
415  return !DoesNameContainWildcard(OldName);
416}
417
418extern bool g_CaseSensitive;
419
420static int CompareTwoNames(const wchar_t *s1, const wchar_t *s2)
421{
422  for (int i = 0;; i++)
423  {
424    wchar_t c1 = s1[i];
425    wchar_t c2 = s2[i];
426    if (c1 == 0 || c2 == 0)
427      return i;
428    if (c1 == c2)
429      continue;
430    if (!g_CaseSensitive && (MyCharUpper(c1) == MyCharUpper(c2)))
431      continue;
432    if (IsCharDirLimiter(c1) && IsCharDirLimiter(c2))
433      continue;
434    return i;
435  }
436}
437
438bool CRenamePair::GetNewPath(bool isFolder, const UString &src, UString &dest) const
439{
440  int num = CompareTwoNames(OldName, src);
441  if (OldName[num] == 0)
442  {
443    if (src[num] != 0 && !IsCharDirLimiter(src[num]) && num != 0 && !IsCharDirLimiter(src[num - 1]))
444      return false;
445  }
446  else
447  {
448    // OldName[num] != 0
449    // OldName = "1\1a.txt"
450    // src = "1"
451
452    if (!isFolder ||
453        src[num] != 0 ||
454        !IsCharDirLimiter(OldName[num]) ||
455        OldName[num + 1] != 0)
456      return false;
457  }
458  dest = NewName + src.Ptr(num);
459  return true;
460}
461
462static int GetReverseSlashPos(const UString &name)
463{
464  int slashPos = name.ReverseFind(L'/');
465  #ifdef _WIN32
466  int slash1Pos = name.ReverseFind(L'\\');
467  slashPos = MyMax(slashPos, slash1Pos);
468  #endif
469  return slashPos;
470}
471
472static HRESULT Compress(
473    const CUpdateOptions &options,
474    CCodecs *codecs,
475    const CActionSet &actionSet,
476    const CArc *arc,
477    CArchivePath &archivePath,
478    const CObjectVector<CArcItem> &arcItems,
479    Byte *processedItemsStatuses,
480    const CDirItems &dirItems,
481    const CDirItem *parentDirItem,
482    CTempFiles &tempFiles,
483    CUpdateErrorInfo &errorInfo,
484    IUpdateCallbackUI *callback)
485{
486  CMyComPtr<IOutArchive> outArchive;
487  int formatIndex = options.MethodMode.Type.FormatIndex;
488  if (arc)
489  {
490    formatIndex = arc->FormatIndex;
491    if (formatIndex < 0)
492      return E_NOTIMPL;
493    CMyComPtr<IInArchive> archive2 = arc->Archive;
494    HRESULT result = archive2.QueryInterface(IID_IOutArchive, &outArchive);
495    if (result != S_OK)
496      throw kUpdateIsNotSupoorted;
497  }
498  else
499  {
500    RINOK(codecs->CreateOutArchive(formatIndex, outArchive));
501
502    #ifdef EXTERNAL_CODECS
503    {
504      CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo;
505      outArchive.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo);
506      if (setCompressCodecsInfo)
507      {
508        RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(codecs));
509      }
510    }
511    #endif
512  }
513  if (outArchive == 0)
514    throw kUpdateIsNotSupoorted;
515
516  NFileTimeType::EEnum fileTimeType;
517  UInt32 value;
518  RINOK(outArchive->GetFileTimeType(&value));
519
520  switch (value)
521  {
522    case NFileTimeType::kWindows:
523    case NFileTimeType::kUnix:
524    case NFileTimeType::kDOS:
525      fileTimeType = (NFileTimeType::EEnum)value;
526      break;
527    default:
528      return E_FAIL;
529  }
530
531  {
532    const CArcInfoEx &arcInfo = codecs->Formats[formatIndex];
533    if (options.AltStreams.Val && !arcInfo.Flags_AltStreams())
534      return E_NOTIMPL;
535    if (options.NtSecurity.Val && !arcInfo.Flags_NtSecure())
536      return E_NOTIMPL;
537  }
538
539  CRecordVector<CUpdatePair2> updatePairs2;
540
541  UStringVector newNames;
542
543  if (options.RenamePairs.Size() != 0)
544  {
545    FOR_VECTOR (i, arcItems)
546    {
547      const CArcItem &ai = arcItems[i];
548      bool needRename = false;
549      UString dest;
550      if (ai.Censored)
551      {
552        FOR_VECTOR (j, options.RenamePairs)
553        {
554          const CRenamePair &rp = options.RenamePairs[j];
555          if (rp.GetNewPath(ai.IsDir, ai.Name, dest))
556          {
557            needRename = true;
558            break;
559          }
560          if (ai.IsAltStream)
561          {
562            int colonPos = ai.Name.ReverseFind(':');
563            int slashPosPos = GetReverseSlashPos(ai.Name);
564            if (colonPos > slashPosPos)
565            {
566              UString mainName = ai.Name.Left(colonPos);
567              /*
568              actually we must improve that code to support cases
569              with folder renaming like: rn arc dir1\ dir2\
570              */
571              if (rp.GetNewPath(false, mainName, dest))
572              {
573                needRename = true;
574                dest += ':';
575                dest += ai.Name.Ptr(colonPos + 1);
576                break;
577              }
578            }
579          }
580        }
581      }
582      CUpdatePair2 up2;
583      up2.SetAs_NoChangeArcItem(ai.IndexInServer);
584      if (needRename)
585      {
586        up2.NewProps = true;
587        RINOK(arc->IsItemAnti(i, up2.IsAnti));
588        up2.NewNameIndex = newNames.Add(dest);
589      }
590      updatePairs2.Add(up2);
591    }
592  }
593  else
594  {
595    CRecordVector<CUpdatePair> updatePairs;
596    GetUpdatePairInfoList(dirItems, arcItems, fileTimeType, updatePairs); // must be done only once!!!
597    // CUpdateProduceCallbackImp upCallback(&arcItems, callback);
598    UpdateProduce(updatePairs, actionSet, updatePairs2, NULL /* &upCallback */);
599  }
600
601  UInt32 numFiles = 0;
602  FOR_VECTOR (i, updatePairs2)
603    if (updatePairs2[i].NewData)
604      numFiles++;
605
606  RINOK(callback->SetNumFiles(numFiles));
607
608  CArchiveUpdateCallback *updateCallbackSpec = new CArchiveUpdateCallback;
609  CMyComPtr<IArchiveUpdateCallback> updateCallback(updateCallbackSpec);
610
611  updateCallbackSpec->ShareForWrite = options.OpenShareForWrite;
612  updateCallbackSpec->StdInMode = options.StdInMode;
613  updateCallbackSpec->Callback = callback;
614
615  if (arc)
616  {
617    // we set Archive to allow to transfer GetProperty requests back to DLL.
618    updateCallbackSpec->Archive = arc->Archive;
619    updateCallbackSpec->GetRawProps = arc->GetRawProps;
620    updateCallbackSpec->GetRootProps = arc->GetRootProps;
621  }
622
623  updateCallbackSpec->DirItems = &dirItems;
624  updateCallbackSpec->ParentDirItem = parentDirItem;
625
626  updateCallbackSpec->StoreNtSecurity = options.NtSecurity.Val;
627  updateCallbackSpec->StoreHardLinks = options.HardLinks.Val;
628  updateCallbackSpec->StoreSymLinks = options.SymLinks.Val;
629
630  updateCallbackSpec->ArcItems = &arcItems;
631  updateCallbackSpec->UpdatePairs = &updatePairs2;
632
633  updateCallbackSpec->ProcessedItemsStatuses = processedItemsStatuses;
634
635  if (options.RenamePairs.Size() != 0)
636    updateCallbackSpec->NewNames = &newNames;
637
638  CMyComPtr<IOutStream> outSeekStream;
639  CMyComPtr<ISequentialOutStream> outStream;
640
641  if (!options.StdOutMode)
642  {
643    FString dirPrefix;
644    if (!GetOnlyDirPrefix(us2fs(archivePath.GetFinalPath()), dirPrefix))
645      throw 1417161;
646    CreateComplexDir(dirPrefix);
647  }
648
649  COutFileStream *outStreamSpec = NULL;
650  COutMultiVolStream *volStreamSpec = NULL;
651
652  if (options.VolumesSizes.Size() == 0)
653  {
654    if (options.StdOutMode)
655      outStream = new CStdOutFileStream;
656    else
657    {
658      outStreamSpec = new COutFileStream;
659      outSeekStream = outStreamSpec;
660      outStream = outSeekStream;
661      bool isOK = false;
662      FString realPath;
663      for (int i = 0; i < (1 << 16); i++)
664      {
665        if (archivePath.Temp)
666        {
667          if (i > 0)
668          {
669            FChar s[16];
670            ConvertUInt32ToString(i, s);
671            archivePath.TempPostfix = s;
672          }
673          realPath = archivePath.GetTempPath();
674        }
675        else
676          realPath = us2fs(archivePath.GetFinalPath());
677        if (outStreamSpec->Create(realPath, false))
678        {
679          tempFiles.Paths.Add(realPath);
680          isOK = true;
681          break;
682        }
683        if (::GetLastError() != ERROR_FILE_EXISTS)
684          break;
685        if (!archivePath.Temp)
686          break;
687      }
688      if (!isOK)
689      {
690        errorInfo.SystemError = ::GetLastError();
691        errorInfo.FileName = realPath;
692        errorInfo.Message = L"7-Zip cannot open file";
693        return E_FAIL;
694      }
695    }
696  }
697  else
698  {
699    if (options.StdOutMode)
700      return E_FAIL;
701    if (arc && arc->GetGlobalOffset() > 0)
702      return E_NOTIMPL;
703
704    volStreamSpec = new COutMultiVolStream;
705    outSeekStream = volStreamSpec;
706    outStream = outSeekStream;
707    volStreamSpec->Sizes = options.VolumesSizes;
708    volStreamSpec->Prefix = us2fs(archivePath.GetFinalVolPath() + L".");
709    volStreamSpec->TempFiles = &tempFiles;
710    volStreamSpec->Init();
711
712    /*
713    updateCallbackSpec->VolumesSizes = volumesSizes;
714    updateCallbackSpec->VolName = archivePath.Prefix + archivePath.Name;
715    if (!archivePath.VolExtension.IsEmpty())
716      updateCallbackSpec->VolExt = UString(L'.') + archivePath.VolExtension;
717    */
718  }
719
720  RINOK(SetProperties(outArchive, options.MethodMode.Properties));
721
722  if (options.SfxMode)
723  {
724    CInFileStream *sfxStreamSpec = new CInFileStream;
725    CMyComPtr<IInStream> sfxStream(sfxStreamSpec);
726    if (!sfxStreamSpec->Open(options.SfxModule))
727    {
728      errorInfo.SystemError = ::GetLastError();
729      errorInfo.Message = L"7-Zip cannot open SFX module";
730      errorInfo.FileName = options.SfxModule;
731      return E_FAIL;
732    }
733
734    CMyComPtr<ISequentialOutStream> sfxOutStream;
735    COutFileStream *outStreamSpec = NULL;
736    if (options.VolumesSizes.Size() == 0)
737      sfxOutStream = outStream;
738    else
739    {
740      outStreamSpec = new COutFileStream;
741      sfxOutStream = outStreamSpec;
742      FString realPath = us2fs(archivePath.GetFinalPath());
743      if (!outStreamSpec->Create(realPath, false))
744      {
745        errorInfo.SystemError = ::GetLastError();
746        errorInfo.FileName = realPath;
747        errorInfo.Message = L"7-Zip cannot open file";
748        return E_FAIL;
749      }
750    }
751    RINOK(NCompress::CopyStream(sfxStream, sfxOutStream, NULL));
752    if (outStreamSpec)
753    {
754      RINOK(outStreamSpec->Close());
755    }
756  }
757
758  CMyComPtr<ISequentialOutStream> tailStream;
759
760  if (options.SfxMode || !arc || arc->ArcStreamOffset == 0)
761    tailStream = outStream;
762  else
763  {
764    // Int64 globalOffset = arc->GetGlobalOffset();
765    RINOK(arc->InStream->Seek(0, STREAM_SEEK_SET, NULL));
766    RINOK(NCompress::CopyStream_ExactSize(arc->InStream, outStream, arc->ArcStreamOffset, NULL));
767    if (options.StdOutMode)
768      tailStream = outStream;
769    else
770    {
771      CTailOutStream *tailStreamSpec = new CTailOutStream;
772      tailStream = tailStreamSpec;
773      tailStreamSpec->Stream = outSeekStream;
774      tailStreamSpec->Offset = arc->ArcStreamOffset;
775      tailStreamSpec->Init();
776    }
777  }
778
779
780  HRESULT result = outArchive->UpdateItems(tailStream, updatePairs2.Size(), updateCallback);
781  callback->Finilize();
782  RINOK(result);
783
784
785  if (options.SetArcMTime)
786  {
787    FILETIME ft;
788    ft.dwLowDateTime = 0;
789    ft.dwHighDateTime = 0;
790    FOR_VECTOR (i, updatePairs2)
791    {
792      CUpdatePair2 &pair2 = updatePairs2[i];
793      const FILETIME *ft2 = NULL;
794      if (pair2.NewProps && pair2.DirIndex >= 0)
795        ft2 = &dirItems.Items[pair2.DirIndex].MTime;
796      else if (pair2.UseArcProps && pair2.ArcIndex >= 0)
797        ft2 = &arcItems[pair2.ArcIndex].MTime;
798      if (ft2)
799      {
800        if (::CompareFileTime(&ft, ft2) < 0)
801          ft = *ft2;
802      }
803    }
804    if (ft.dwLowDateTime != 0 || ft.dwHighDateTime != 0)
805    {
806      if (outStreamSpec)
807        outStreamSpec->SetMTime(&ft);
808      else if (volStreamSpec)
809        volStreamSpec->SetMTime(&ft);;
810    }
811  }
812
813  if (outStreamSpec)
814    result = outStreamSpec->Close();
815  else if (volStreamSpec)
816    result = volStreamSpec->Close();
817  return result;
818}
819
820static HRESULT EnumerateInArchiveItems(
821    // bool storeStreamsMode,
822    const NWildcard::CCensor &censor,
823    const CArc &arc,
824    CObjectVector<CArcItem> &arcItems)
825{
826  arcItems.Clear();
827  UInt32 numItems;
828  IInArchive *archive = arc.Archive;
829  RINOK(archive->GetNumberOfItems(&numItems));
830  arcItems.ClearAndReserve(numItems);
831  for (UInt32 i = 0; i < numItems; i++)
832  {
833    CArcItem ai;
834
835    RINOK(arc.GetItemPath(i, ai.Name));
836    RINOK(Archive_IsItem_Folder(archive, i, ai.IsDir));
837    RINOK(Archive_IsItem_AltStream(archive, i, ai.IsAltStream));
838    /*
839    if (!storeStreamsMode && ai.IsAltStream)
840      continue;
841    */
842    ai.Censored = censor.CheckPath(ai.IsAltStream, ai.Name, !ai.IsDir);
843    RINOK(arc.GetItemMTime(i, ai.MTime, ai.MTimeDefined));
844    RINOK(arc.GetItemSize(i, ai.Size, ai.SizeDefined));
845
846    {
847      CPropVariant prop;
848      RINOK(archive->GetProperty(i, kpidTimeType, &prop));
849      if (prop.vt == VT_UI4)
850      {
851        ai.TimeType = (int)(NFileTimeType::EEnum)prop.ulVal;
852        switch (ai.TimeType)
853        {
854          case NFileTimeType::kWindows:
855          case NFileTimeType::kUnix:
856          case NFileTimeType::kDOS:
857            break;
858          default:
859            return E_FAIL;
860        }
861      }
862    }
863
864    ai.IndexInServer = i;
865    arcItems.AddInReserved(ai);
866  }
867  return S_OK;
868}
869
870struct CEnumDirItemUpdateCallback: public IEnumDirItemCallback
871{
872  IUpdateCallbackUI2 *Callback;
873  HRESULT ScanProgress(UInt64 numFolders, UInt64 numFiles, UInt64 totalSize, const wchar_t *path, bool isDir)
874  {
875    return Callback->ScanProgress(numFolders, numFiles, totalSize, path, isDir);
876  }
877};
878
879#if defined(_WIN32) && !defined(UNDER_CE)
880
881#include <mapi.h>
882
883#endif
884
885struct CRefSortPair
886{
887  int Len;
888  int Index;
889};
890
891#define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; }
892
893static int CompareRefSortPair(const CRefSortPair *a1, const CRefSortPair *a2, void *)
894{
895  RINOZ(-MyCompare(a1->Len, a2->Len));
896  return MyCompare(a1->Index, a2->Index);
897}
898
899static int GetNumSlashes(const FChar *s)
900{
901  for (int numSlashes = 0;;)
902  {
903    FChar c = *s++;
904    if (c == 0)
905      return numSlashes;
906    if (
907        #ifdef _WIN32
908        c == FTEXT('\\') ||
909        #endif
910        c == FTEXT('/'))
911      numSlashes++;
912  }
913}
914
915#ifdef _WIN32
916void ConvertToLongNames(NWildcard::CCensor &censor);
917#endif
918
919HRESULT UpdateArchive(
920    CCodecs *codecs,
921    const CObjectVector<COpenType> &types,
922    const UString &cmdArcPath2,
923    NWildcard::CCensor &censor,
924    CUpdateOptions &options,
925    CUpdateErrorInfo &errorInfo,
926    IOpenCallbackUI *openCallback,
927    IUpdateCallbackUI2 *callback,
928    bool needSetPath)
929{
930  if (options.StdOutMode && options.EMailMode)
931    return E_FAIL;
932
933  if (types.Size() > 1)
934    return E_NOTIMPL;
935
936  bool renameMode = !options.RenamePairs.IsEmpty();
937  if (renameMode)
938  {
939    if (options.Commands.Size() != 1)
940      return E_FAIL;
941  }
942
943  if (options.DeleteAfterCompressing)
944  {
945    if (options.Commands.Size() != 1)
946      return E_NOTIMPL;
947    const CActionSet &as = options.Commands[0].ActionSet;
948    for (int i = 2; i < NPairState::kNumValues; i++)
949      if (as.StateActions[i] != NPairAction::kCompress)
950        return E_NOTIMPL;
951  }
952
953  censor.AddPathsToCensor(options.PathMode);
954  #ifdef _WIN32
955  ConvertToLongNames(censor);
956  #endif
957  censor.ExtendExclude();
958
959
960  if (options.VolumesSizes.Size() > 0 && (options.EMailMode /* || options.SfxMode */))
961    return E_NOTIMPL;
962
963  if (options.SfxMode)
964  {
965    CProperty property;
966    property.Name = L"rsfx";
967    property.Value = L"on";
968    options.MethodMode.Properties.Add(property);
969    if (options.SfxModule.IsEmpty())
970    {
971      errorInfo.Message = L"SFX file is not specified";
972      return E_FAIL;
973    }
974    bool found = false;
975    if (options.SfxModule.Find(FCHAR_PATH_SEPARATOR) < 0)
976    {
977      const FString fullName = NDLL::GetModuleDirPrefix() + options.SfxModule;
978      if (NFind::DoesFileExist(fullName))
979      {
980        options.SfxModule = fullName;
981        found = true;
982      }
983    }
984    if (!found)
985    {
986      if (!NFind::DoesFileExist(options.SfxModule))
987      {
988        errorInfo.SystemError = ::GetLastError();
989        errorInfo.Message = L"7-Zip cannot find specified SFX module";
990        errorInfo.FileName = options.SfxModule;
991        return E_FAIL;
992      }
993    }
994  }
995
996  CArchiveLink arcLink;
997
998
999  if (needSetPath)
1000  {
1001    if (!options.InitFormatIndex(codecs, types, cmdArcPath2) ||
1002        !options.SetArcPath(codecs, cmdArcPath2))
1003      return E_NOTIMPL;
1004  }
1005  UString arcPath = options.ArchivePath.GetFinalPath();
1006
1007  if (cmdArcPath2.IsEmpty())
1008  {
1009    if (options.MethodMode.Type.FormatIndex < 0)
1010      throw "type of archive is not specified";
1011  }
1012  else
1013  {
1014    NFind::CFileInfo fi;
1015    if (!fi.Find(us2fs(arcPath)))
1016    {
1017      if (renameMode)
1018        throw "can't find archive";;
1019      if (options.MethodMode.Type.FormatIndex < 0)
1020      {
1021        if (!options.SetArcPath(codecs, cmdArcPath2))
1022          return E_NOTIMPL;
1023      }
1024    }
1025    else
1026    {
1027      if (fi.IsDir())
1028        throw "there is no such archive";
1029      if (fi.IsDevice)
1030        return E_NOTIMPL;
1031      if (options.VolumesSizes.Size() > 0)
1032        return E_NOTIMPL;
1033      CObjectVector<COpenType> types;
1034      // change it.
1035      if (options.MethodMode.Type_Defined)
1036        types.Add(options.MethodMode.Type);
1037      // We need to set Properties to open archive only in some cases (WIM archives).
1038
1039      CIntVector excl;
1040      COpenOptions op;
1041      #ifndef _SFX
1042      op.props = &options.MethodMode.Properties;
1043      #endif
1044      op.codecs = codecs;
1045      op.types = &types;
1046      op.excludedFormats = &excl;
1047      op.stdInMode = false;
1048      op.stream = NULL;
1049      op.filePath = arcPath;
1050
1051      HRESULT result = arcLink.Open2(op, openCallback);
1052
1053      if (result == E_ABORT)
1054        return result;
1055
1056      const wchar_t *errorArcType = NULL;
1057      if (arcLink.NonOpen_ErrorInfo.ErrorFormatIndex > 0)
1058        errorArcType = codecs->Formats[arcLink.NonOpen_ErrorInfo.ErrorFormatIndex].Name;
1059      RINOK(callback->OpenResult(arcPath, result, errorArcType));
1060      /*
1061      if (result == S_FALSE)
1062        return E_FAIL;
1063      */
1064      RINOK(result);
1065      if (arcLink.VolumePaths.Size() > 1)
1066      {
1067        errorInfo.SystemError = (DWORD)E_NOTIMPL;
1068        errorInfo.Message = L"Updating for multivolume archives is not implemented";
1069        return E_NOTIMPL;
1070      }
1071
1072      CArc &arc = arcLink.Arcs.Back();
1073      arc.MTimeDefined = !fi.IsDevice;
1074      arc.MTime = fi.MTime;
1075
1076      if (arc.ErrorInfo.ThereIsTail)
1077      {
1078        errorInfo.SystemError = (DWORD)E_NOTIMPL;
1079        errorInfo.Message = L"There is some data block after the end of the archive";
1080        return E_NOTIMPL;
1081      }
1082      if (options.MethodMode.Type.FormatIndex < 0)
1083      {
1084        options.MethodMode.Type.FormatIndex = arcLink.GetArc()->FormatIndex;
1085        if (!options.SetArcPath(codecs, cmdArcPath2))
1086          return E_NOTIMPL;
1087      }
1088    }
1089  }
1090
1091  if (options.MethodMode.Type.FormatIndex < 0)
1092  {
1093    options.MethodMode.Type.FormatIndex = codecs->FindFormatForArchiveType(kDefaultArcType);
1094    if (options.MethodMode.Type.FormatIndex < 0)
1095      return E_NOTIMPL;
1096  }
1097
1098  bool thereIsInArchive = arcLink.IsOpen;
1099  if (!thereIsInArchive && renameMode)
1100    return E_FAIL;
1101
1102  CDirItems dirItems;
1103  CDirItem parentDirItem;
1104  CDirItem *parentDirItem_Ptr = NULL;
1105
1106  /*
1107  FStringVector requestedPaths;
1108  FStringVector *requestedPaths_Ptr = NULL;
1109  if (options.DeleteAfterCompressing)
1110    requestedPaths_Ptr = &requestedPaths;
1111  */
1112
1113  if (options.StdInMode)
1114  {
1115    CDirItem di;
1116    di.Name = options.StdInFileName;
1117    di.Size = (UInt64)(Int64)-1;
1118    di.Attrib = 0;
1119    NTime::GetCurUtcFileTime(di.MTime);
1120    di.CTime = di.ATime = di.MTime;
1121    dirItems.Items.Add(di);
1122  }
1123  else
1124  {
1125    bool needScanning = false;
1126    if (!renameMode)
1127    FOR_VECTOR (i, options.Commands)
1128      if (options.Commands[i].ActionSet.NeedScanning())
1129        needScanning = true;
1130    if (needScanning)
1131    {
1132      CEnumDirItemUpdateCallback enumCallback;
1133      enumCallback.Callback = callback;
1134      RINOK(callback->StartScanning());
1135
1136      dirItems.SymLinks = options.SymLinks.Val;
1137
1138      #if defined(_WIN32) && !defined(UNDER_CE)
1139      dirItems.ReadSecure = options.NtSecurity.Val;
1140      #endif
1141
1142      dirItems.ScanAltStreams = options.AltStreams.Val;
1143      HRESULT res = EnumerateItems(censor,
1144          options.PathMode,
1145          options.AddPathPrefix,
1146          dirItems, &enumCallback);
1147      FOR_VECTOR (i, dirItems.ErrorPaths)
1148      {
1149        RINOK(callback->CanNotFindError(fs2us(dirItems.ErrorPaths[i]), dirItems.ErrorCodes[i]));
1150      }
1151      if (res != S_OK)
1152      {
1153        if (res != E_ABORT)
1154          errorInfo.Message = L"Scanning error";
1155        return res;
1156      }
1157      RINOK(callback->FinishScanning());
1158
1159      if (censor.Pairs.Size() == 1)
1160      {
1161        NFind::CFileInfo fi;
1162        FString prefix = us2fs(censor.Pairs[0].Prefix) + FTEXT(".");
1163        // UString prefix = censor.Pairs[0].Prefix;
1164        /*
1165        if (prefix.Back() == WCHAR_PATH_SEPARATOR)
1166        {
1167          prefix.DeleteBack();
1168        }
1169        */
1170        if (fi.Find(prefix))
1171          if (fi.IsDir())
1172          {
1173            parentDirItem.Size = fi.Size;
1174            parentDirItem.CTime = fi.CTime;
1175            parentDirItem.ATime = fi.ATime;
1176            parentDirItem.MTime = fi.MTime;
1177            parentDirItem.Attrib = fi.Attrib;
1178            parentDirItem_Ptr = &parentDirItem;
1179
1180            int secureIndex = -1;
1181            #if defined(_WIN32) && !defined(UNDER_CE)
1182            if (options.NtSecurity.Val)
1183              dirItems.AddSecurityItem(prefix, secureIndex);
1184            #endif
1185            parentDirItem.SecureIndex = secureIndex;
1186
1187            parentDirItem_Ptr = &parentDirItem;
1188          }
1189      }
1190    }
1191  }
1192
1193  FString tempDirPrefix;
1194  bool usesTempDir = false;
1195
1196  #ifdef _WIN32
1197  CTempDir tempDirectory;
1198  if (options.EMailMode && options.EMailRemoveAfter)
1199  {
1200    tempDirectory.Create(kTempFolderPrefix);
1201    tempDirPrefix = tempDirectory.GetPath();
1202    NormalizeDirPathPrefix(tempDirPrefix);
1203    usesTempDir = true;
1204  }
1205  #endif
1206
1207  CTempFiles tempFiles;
1208
1209  bool createTempFile = false;
1210
1211  if (!options.StdOutMode && options.UpdateArchiveItself)
1212  {
1213    CArchivePath &ap = options.Commands[0].ArchivePath;
1214    ap = options.ArchivePath;
1215    // if ((archive != 0 && !usesTempDir) || !options.WorkingDir.IsEmpty())
1216    if ((thereIsInArchive || !options.WorkingDir.IsEmpty()) && !usesTempDir && options.VolumesSizes.Size() == 0)
1217    {
1218      createTempFile = true;
1219      ap.Temp = true;
1220      if (!options.WorkingDir.IsEmpty())
1221        ap.TempPrefix = options.WorkingDir;
1222      else
1223        ap.TempPrefix = us2fs(ap.Prefix);
1224      NormalizeDirPathPrefix(ap.TempPrefix);
1225    }
1226  }
1227
1228  unsigned i;
1229  for (i = 0; i < options.Commands.Size(); i++)
1230  {
1231    CArchivePath &ap = options.Commands[i].ArchivePath;
1232    if (usesTempDir)
1233    {
1234      // Check it
1235      ap.Prefix = fs2us(tempDirPrefix);
1236      // ap.Temp = true;
1237      // ap.TempPrefix = tempDirPrefix;
1238    }
1239    if (!options.StdOutMode &&
1240        (i > 0 || !createTempFile))
1241    {
1242      const FString path = us2fs(ap.GetFinalPath());
1243      if (NFind::DoesFileOrDirExist(path))
1244      {
1245        errorInfo.SystemError = 0;
1246        errorInfo.Message = L"The file already exists";
1247        errorInfo.FileName = path;
1248        return E_FAIL;
1249      }
1250    }
1251  }
1252
1253  CObjectVector<CArcItem> arcItems;
1254  if (thereIsInArchive)
1255  {
1256    RINOK(EnumerateInArchiveItems(
1257      // options.StoreAltStreams,
1258      censor, arcLink.Arcs.Back(), arcItems));
1259  }
1260
1261  /*
1262  FStringVector processedFilePaths;
1263  FStringVector *processedFilePaths_Ptr = NULL;
1264  if (options.DeleteAfterCompressing)
1265    processedFilePaths_Ptr = &processedFilePaths;
1266  */
1267
1268  CByteBuffer processedItems;
1269  if (options.DeleteAfterCompressing)
1270  {
1271    unsigned num = dirItems.Items.Size();
1272    processedItems.Alloc(num);
1273    for (i = 0; i < num; i++)
1274      processedItems[i] = 0;
1275  }
1276
1277  for (i = 0; i < options.Commands.Size(); i++)
1278  {
1279    const CArc *arc = thereIsInArchive ? arcLink.GetArc() : 0;
1280    // IInArchiveExtra *archiveExtra = thereIsInArchive ? arcLink.GetArchiveExtra() : 0;
1281    // IArchiveGetRootProps *archiveGetRootProps = thereIsInArchive ? arcLink.GetArchiveGetRootProps() : 0;
1282    CUpdateArchiveCommand &command = options.Commands[i];
1283    UString name;
1284    bool isUpdating;
1285    if (options.StdOutMode)
1286    {
1287      name = L"stdout";
1288      isUpdating = arc != 0;
1289    }
1290    else
1291    {
1292      name = command.ArchivePath.GetFinalPath();
1293      isUpdating = (i == 0 && options.UpdateArchiveItself && arc != 0);
1294    }
1295    RINOK(callback->StartArchive(name, isUpdating))
1296
1297    RINOK(Compress(options,
1298        codecs,
1299        command.ActionSet,
1300        arc,
1301        command.ArchivePath,
1302        arcItems,
1303        options.DeleteAfterCompressing ? (Byte *)processedItems : NULL,
1304
1305        dirItems,
1306        parentDirItem_Ptr,
1307
1308        tempFiles,
1309        errorInfo, callback));
1310
1311    RINOK(callback->FinishArchive());
1312  }
1313
1314
1315  if (thereIsInArchive)
1316  {
1317    RINOK(arcLink.Close());
1318    arcLink.Release();
1319  }
1320
1321  tempFiles.Paths.Clear();
1322  if (createTempFile)
1323  {
1324    try
1325    {
1326      CArchivePath &ap = options.Commands[0].ArchivePath;
1327      const FString &tempPath = ap.GetTempPath();
1328      if (thereIsInArchive)
1329        if (!DeleteFileAlways(us2fs(arcPath)))
1330        {
1331          errorInfo.SystemError = ::GetLastError();
1332          errorInfo.Message = L"7-Zip cannot delete the file";
1333          errorInfo.FileName = us2fs(arcPath);
1334          return E_FAIL;
1335        }
1336      if (!MyMoveFile(tempPath, us2fs(arcPath)))
1337      {
1338        errorInfo.SystemError = ::GetLastError();
1339        errorInfo.Message = L"7-Zip cannot move the file";
1340        errorInfo.FileName = tempPath;
1341        errorInfo.FileName2 = us2fs(arcPath);
1342        return E_FAIL;
1343      }
1344    }
1345    catch(...)
1346    {
1347      throw;
1348    }
1349  }
1350
1351
1352  #if defined(_WIN32) && !defined(UNDER_CE)
1353  if (options.EMailMode)
1354  {
1355    NDLL::CLibrary mapiLib;
1356    if (!mapiLib.Load(FTEXT("Mapi32.dll")))
1357    {
1358      errorInfo.SystemError = ::GetLastError();
1359      errorInfo.Message = L"7-Zip cannot load Mapi32.dll";
1360      return E_FAIL;
1361    }
1362
1363    /*
1364    LPMAPISENDDOCUMENTS fnSend = (LPMAPISENDDOCUMENTS)mapiLib.GetProc("MAPISendDocuments");
1365    if (fnSend == 0)
1366    {
1367      errorInfo.SystemError = ::GetLastError();
1368      errorInfo.Message = L"7-Zip cannot find MAPISendDocuments function";
1369      return E_FAIL;
1370    }
1371    */
1372    LPMAPISENDMAIL sendMail = (LPMAPISENDMAIL)mapiLib.GetProc("MAPISendMail");
1373    if (sendMail == 0)
1374    {
1375      errorInfo.SystemError = ::GetLastError();
1376      errorInfo.Message = L"7-Zip cannot find MAPISendMail function";
1377      return E_FAIL;
1378    }
1379
1380    FStringVector fullPaths;
1381    unsigned i;
1382    for (i = 0; i < options.Commands.Size(); i++)
1383    {
1384      CArchivePath &ap = options.Commands[i].ArchivePath;
1385      FString arcPath;
1386      if (!MyGetFullPathName(us2fs(ap.GetFinalPath()), arcPath))
1387      {
1388        errorInfo.SystemError = ::GetLastError();
1389        errorInfo.Message = L"GetFullPathName error";
1390        return E_FAIL;
1391      }
1392      fullPaths.Add(arcPath);
1393    }
1394    CCurrentDirRestorer curDirRestorer;
1395    for (i = 0; i < fullPaths.Size(); i++)
1396    {
1397      UString arcPath = fs2us(fullPaths[i]);
1398      UString fileName = ExtractFileNameFromPath(arcPath);
1399      AString path = GetAnsiString(arcPath);
1400      AString name = GetAnsiString(fileName);
1401      // Warning!!! MAPISendDocuments function changes Current directory
1402      // fnSend(0, ";", (LPSTR)(LPCSTR)path, (LPSTR)(LPCSTR)name, 0);
1403
1404      MapiFileDesc f;
1405      memset(&f, 0, sizeof(f));
1406      f.nPosition = 0xFFFFFFFF;
1407      f.lpszPathName = (char *)(const char *)path;
1408      f.lpszFileName = (char *)(const char *)name;
1409
1410      MapiMessage m;
1411      memset(&m, 0, sizeof(m));
1412      m.nFileCount = 1;
1413      m.lpFiles = &f;
1414
1415      const AString addr = GetAnsiString(options.EMailAddress);
1416      MapiRecipDesc rec;
1417      if (!addr.IsEmpty())
1418      {
1419        memset(&rec, 0, sizeof(rec));
1420        rec.ulRecipClass = MAPI_TO;
1421        rec.lpszAddress = (char *)(const char *)addr;
1422        m.nRecipCount = 1;
1423        m.lpRecips = &rec;
1424      }
1425
1426      sendMail((LHANDLE)0, 0, &m, MAPI_DIALOG, 0);
1427    }
1428  }
1429  #endif
1430
1431  if (options.DeleteAfterCompressing)
1432  {
1433    CRecordVector<CRefSortPair> pairs;
1434    FStringVector foldersNames;
1435    for (i = 0; i < dirItems.Items.Size(); i++)
1436    {
1437      const CDirItem &dirItem = dirItems.Items[i];
1438      FString phyPath = us2fs(dirItems.GetPhyPath(i));
1439      if (dirItem.IsDir())
1440      {
1441        CRefSortPair pair;
1442        pair.Index = i;
1443        pair.Len = GetNumSlashes(phyPath);
1444        pairs.Add(pair);
1445      }
1446      else
1447      {
1448        if (processedItems[i] != 0 || dirItem.Size == 0)
1449        {
1450          DeleteFileAlways(phyPath);
1451        }
1452        else
1453        {
1454          // file was skipped
1455          /*
1456          errorInfo.SystemError = 0;
1457          errorInfo.Message = L"file was not processed";
1458          errorInfo.FileName = phyPath;
1459          return E_FAIL;
1460          */
1461        }
1462      }
1463    }
1464
1465    pairs.Sort(CompareRefSortPair, NULL);
1466    for (i = 0; i < pairs.Size(); i++)
1467    {
1468      FString phyPath = us2fs(dirItems.GetPhyPath(pairs[i].Index));
1469      if (NFind::DoesDirExist(phyPath))
1470      {
1471        // printf("delete %S\n", phyPath);
1472        RemoveDir(phyPath);
1473      }
1474    }
1475  }
1476  return S_OK;
1477}
1478