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