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 static 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 
     67 using namespace NUpdateArchive;
     68 
     69 class 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;
     87 public:
     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 
    113 HRESULT 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 
    129 bool 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 
    142 STDMETHODIMP 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 
    220 STDMETHODIMP 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 
    237 STDMETHODIMP 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 
    268 void 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 
    301 UString CArchivePath::GetFinalPath() const
    302 {
    303   UString path = GetPathWithoutExt();
    304   if (!BaseExtension.IsEmpty())
    305     path += UString(L'.') + BaseExtension;
    306   return path;
    307 }
    308 
    309 UString CArchivePath::GetFinalVolPath() const
    310 {
    311   UString path = GetPathWithoutExt();
    312   if (!BaseExtension.IsEmpty())
    313     path += UString(L'.') + VolExtension;
    314   return path;
    315 }
    316 
    317 FString 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 
    327 static const wchar_t *kDefaultArcType = L"7z";
    328 static const wchar_t *kDefaultArcExt = L"7z";
    329 static const wchar_t *kSFXExtension =
    330   #ifdef _WIN32
    331     L"exe";
    332   #else
    333     L"";
    334   #endif
    335 
    336 bool 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 
    361 bool 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 /*
    393 struct 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 
    403 HRESULT CUpdateProduceCallbackImp::ShowDeleteFile(int arcIndex)
    404 {
    405   return _callback->ShowDeleteFile((*_arcItems)[arcIndex].Name);
    406 }
    407 */
    408 
    409 bool CRenamePair::Prepare()
    410 {
    411   if (RecursedType != NRecursedType::kNonRecursed)
    412     return false;
    413   if (!WildcardParsing)
    414     return true;
    415   return !DoesNameContainWildcard(OldName);
    416 }
    417 
    418 extern bool g_CaseSensitive;
    419 
    420 static 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 
    438 bool 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 
    462 static 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 
    472 static 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 
    820 static 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 
    870 struct 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 
    885 struct CRefSortPair
    886 {
    887   int Len;
    888   int Index;
    889 };
    890 
    891 #define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; }
    892 
    893 static 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 
    899 static 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
    916 void ConvertToLongNames(NWildcard::CCensor &censor);
    917 #endif
    918 
    919 HRESULT 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