misc/libphysfs/lzma/CPP/7zip/UI/Common/Update.cpp
changeset 13881 99b265e0d1d0
parent 13880 5f819b90d479
child 13882 b172a5d40eee
equal deleted inserted replaced
13880:5f819b90d479 13881:99b265e0d1d0
     1 // Update.cpp
       
     2 
       
     3 #include "StdAfx.h"
       
     4 
       
     5 #ifdef _WIN32
       
     6 #include <mapi.h>
       
     7 #endif
       
     8 
       
     9 #include "Update.h"
       
    10 
       
    11 #include "Common/IntToString.h"
       
    12 #include "Common/StringConvert.h"
       
    13 #include "Common/CommandLineParser.h"
       
    14 
       
    15 #ifdef _WIN32
       
    16 #include "Windows/DLL.h"
       
    17 #endif
       
    18 
       
    19 #include "Windows/Defs.h"
       
    20 #include "Windows/FileDir.h"
       
    21 #include "Windows/FileFind.h"
       
    22 #include "Windows/FileName.h"
       
    23 #include "Windows/PropVariant.h"
       
    24 #include "Windows/PropVariantConversions.h"
       
    25 // #include "Windows/Synchronization.h"
       
    26 
       
    27 #include "../../Common/FileStreams.h"
       
    28 #include "../../Compress/Copy/CopyCoder.h"
       
    29 
       
    30 #include "../Common/DirItem.h"
       
    31 #include "../Common/EnumDirItems.h"
       
    32 #include "../Common/UpdateProduce.h"
       
    33 #include "../Common/OpenArchive.h"
       
    34 
       
    35 #include "TempFiles.h"
       
    36 #include "UpdateCallback.h"
       
    37 #include "EnumDirItems.h"
       
    38 #include "SetProperties.h"
       
    39 
       
    40 static const char *kUpdateIsNotSupoorted = 
       
    41   "update operations are not supported for this archive";
       
    42 
       
    43 using namespace NCommandLineParser;
       
    44 using namespace NWindows;
       
    45 using namespace NCOM;
       
    46 using namespace NFile;
       
    47 using namespace NName;
       
    48 
       
    49 static const wchar_t *kTempFolderPrefix = L"7zE";
       
    50 
       
    51 using namespace NUpdateArchive;
       
    52 
       
    53 static HRESULT CopyBlock(ISequentialInStream *inStream, ISequentialOutStream *outStream)
       
    54 {
       
    55   CMyComPtr<ICompressCoder> copyCoder = new NCompress::CCopyCoder;
       
    56   return copyCoder->Code(inStream, outStream, NULL, NULL, NULL);
       
    57 }
       
    58 
       
    59 class COutMultiVolStream: 
       
    60   public IOutStream,
       
    61   public CMyUnknownImp
       
    62 {
       
    63   int _streamIndex; // required stream
       
    64   UInt64 _offsetPos; // offset from start of _streamIndex index
       
    65   UInt64 _absPos;
       
    66   UInt64 _length;
       
    67 
       
    68   struct CSubStreamInfo
       
    69   {
       
    70     COutFileStream *StreamSpec;
       
    71     CMyComPtr<IOutStream> Stream;
       
    72     UString Name;
       
    73     UInt64 Pos;
       
    74     UInt64 RealSize;
       
    75   };
       
    76   CObjectVector<CSubStreamInfo> Streams;
       
    77 public:
       
    78   // CMyComPtr<IArchiveUpdateCallback2> VolumeCallback;
       
    79   CRecordVector<UInt64> Sizes;
       
    80   UString Prefix;
       
    81   CTempFiles *TempFiles;
       
    82 
       
    83   void Init()
       
    84   {
       
    85     _streamIndex = 0;
       
    86     _offsetPos = 0;
       
    87     _absPos = 0;
       
    88     _length = 0;
       
    89   }
       
    90 
       
    91   HRESULT Close(); 
       
    92 
       
    93   MY_UNKNOWN_IMP1(IOutStream)
       
    94 
       
    95   STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
       
    96   STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition);
       
    97   STDMETHOD(SetSize)(Int64 newSize);
       
    98 };
       
    99 
       
   100 // static NSynchronization::CCriticalSection g_TempPathsCS;
       
   101 
       
   102 HRESULT COutMultiVolStream::Close()
       
   103 {
       
   104   HRESULT res = S_OK;
       
   105   for (int i = 0; i < Streams.Size(); i++)
       
   106   {
       
   107     CSubStreamInfo &s = Streams[i];
       
   108     if (s.StreamSpec)
       
   109     {
       
   110       HRESULT res2 = s.StreamSpec->Close();
       
   111       if (res2 != S_OK)
       
   112         res = res2;
       
   113     }
       
   114   }
       
   115   return res;
       
   116 }
       
   117 
       
   118 STDMETHODIMP COutMultiVolStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
       
   119 {
       
   120   if(processedSize != NULL)
       
   121     *processedSize = 0;
       
   122   while(size > 0)
       
   123   {
       
   124     if (_streamIndex >= Streams.Size())
       
   125     {
       
   126       CSubStreamInfo subStream;
       
   127 
       
   128       wchar_t temp[32];
       
   129       ConvertUInt64ToString(_streamIndex + 1, temp);
       
   130       UString res = temp;
       
   131       while (res.Length() < 3)
       
   132         res = UString(L'0') + res;
       
   133       UString name = Prefix + res;
       
   134       subStream.StreamSpec = new COutFileStream;
       
   135       subStream.Stream = subStream.StreamSpec;
       
   136       if(!subStream.StreamSpec->Create(name, false))
       
   137         return ::GetLastError();
       
   138       {
       
   139         // NSynchronization::CCriticalSectionLock lock(g_TempPathsCS);
       
   140         TempFiles->Paths.Add(name);
       
   141       }
       
   142 
       
   143       subStream.Pos = 0;
       
   144       subStream.RealSize = 0;
       
   145       subStream.Name = name;
       
   146       Streams.Add(subStream);
       
   147       continue;
       
   148     }
       
   149     CSubStreamInfo &subStream = Streams[_streamIndex];
       
   150 
       
   151     int index = _streamIndex;
       
   152     if (index >= Sizes.Size())
       
   153       index = Sizes.Size() - 1;
       
   154     UInt64 volSize = Sizes[index];
       
   155 
       
   156     if (_offsetPos >= volSize)
       
   157     {
       
   158       _offsetPos -= volSize;
       
   159       _streamIndex++;
       
   160       continue;
       
   161     }
       
   162     if (_offsetPos != subStream.Pos)
       
   163     {
       
   164       // CMyComPtr<IOutStream> outStream;
       
   165       // RINOK(subStream.Stream.QueryInterface(IID_IOutStream, &outStream));
       
   166       RINOK(subStream.Stream->Seek(_offsetPos, STREAM_SEEK_SET, NULL));
       
   167       subStream.Pos = _offsetPos;
       
   168     }
       
   169 
       
   170     UInt32 curSize = (UInt32)MyMin((UInt64)size, volSize - subStream.Pos);
       
   171     UInt32 realProcessed;
       
   172     RINOK(subStream.Stream->Write(data, curSize, &realProcessed));
       
   173     data = (void *)((Byte *)data + realProcessed);
       
   174     size -= realProcessed;
       
   175     subStream.Pos += realProcessed;
       
   176     _offsetPos += realProcessed;
       
   177     _absPos += realProcessed;
       
   178     if (_absPos > _length)
       
   179       _length = _absPos;
       
   180     if (_offsetPos > subStream.RealSize)
       
   181       subStream.RealSize = _offsetPos;
       
   182     if(processedSize != NULL)
       
   183       *processedSize += realProcessed;
       
   184     if (subStream.Pos == volSize)
       
   185     {
       
   186       _streamIndex++;
       
   187       _offsetPos = 0;
       
   188     }
       
   189     if (realProcessed == 0 && curSize != 0)
       
   190       return E_FAIL;
       
   191     break;
       
   192   }
       
   193   return S_OK;
       
   194 }
       
   195 
       
   196 STDMETHODIMP COutMultiVolStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)
       
   197 {
       
   198   if(seekOrigin >= 3)
       
   199     return STG_E_INVALIDFUNCTION;
       
   200   switch(seekOrigin)
       
   201   {
       
   202     case STREAM_SEEK_SET:
       
   203       _absPos = offset;
       
   204       break;
       
   205     case STREAM_SEEK_CUR:
       
   206       _absPos += offset;
       
   207       break;
       
   208     case STREAM_SEEK_END:
       
   209       _absPos = _length + offset;
       
   210       break;
       
   211   }
       
   212   _offsetPos = _absPos;
       
   213   if (newPosition != NULL)
       
   214     *newPosition = _absPos;
       
   215   _streamIndex = 0;
       
   216   return S_OK;
       
   217 }
       
   218 
       
   219 STDMETHODIMP COutMultiVolStream::SetSize(Int64 newSize)
       
   220 {
       
   221   if (newSize < 0)
       
   222     return E_INVALIDARG;
       
   223   int i = 0;
       
   224   while (i < Streams.Size())
       
   225   {
       
   226     CSubStreamInfo &subStream = Streams[i++];
       
   227     if ((UInt64)newSize < subStream.RealSize)
       
   228     {
       
   229       RINOK(subStream.Stream->SetSize(newSize));
       
   230       subStream.RealSize = newSize;
       
   231       break;
       
   232     }
       
   233     newSize -= subStream.RealSize;
       
   234   }
       
   235   while (i < Streams.Size())
       
   236   {
       
   237     {
       
   238       CSubStreamInfo &subStream = Streams.Back();
       
   239       subStream.Stream.Release();
       
   240       NDirectory::DeleteFileAlways(subStream.Name);
       
   241     }
       
   242     Streams.DeleteBack();
       
   243   }
       
   244   _offsetPos = _absPos;
       
   245   _streamIndex = 0;
       
   246   _length = newSize;
       
   247   return S_OK;
       
   248 }
       
   249 
       
   250 static const wchar_t *kDefaultArchiveType = L"7z";
       
   251 static const wchar_t *kSFXExtension =
       
   252   #ifdef _WIN32
       
   253     L"exe";
       
   254   #else
       
   255     L"";
       
   256   #endif
       
   257 
       
   258 bool CUpdateOptions::Init(const CCodecs *codecs, const UString &arcPath, const UString &arcType)
       
   259 {
       
   260   if (!arcType.IsEmpty())
       
   261     MethodMode.FormatIndex = codecs->FindFormatForArchiveType(arcType);
       
   262   else
       
   263   {
       
   264     MethodMode.FormatIndex = codecs->FindFormatForArchiveName(arcPath);
       
   265     if (MethodMode.FormatIndex < 0)
       
   266       MethodMode.FormatIndex = codecs->FindFormatForArchiveType(kDefaultArchiveType);
       
   267   }
       
   268   if (MethodMode.FormatIndex < 0)
       
   269     return false;
       
   270   const CArcInfoEx &arcInfo = codecs->Formats[MethodMode.FormatIndex];
       
   271   UString typeExt = arcInfo.GetMainExt();
       
   272   UString ext = typeExt;
       
   273   if (SfxMode)
       
   274     ext = kSFXExtension;
       
   275   ArchivePath.BaseExtension = ext;
       
   276   ArchivePath.VolExtension = typeExt;
       
   277   ArchivePath.ParseFromPath(arcPath);
       
   278   for (int i = 0; i < Commands.Size(); i++)
       
   279   {
       
   280     CUpdateArchiveCommand &uc = Commands[i];
       
   281     uc.ArchivePath.BaseExtension = ext;
       
   282     uc.ArchivePath.VolExtension = typeExt;
       
   283     uc.ArchivePath.ParseFromPath(uc.UserArchivePath);
       
   284   }
       
   285   return true;
       
   286 }
       
   287 
       
   288 
       
   289 static HRESULT Compress(
       
   290     CCodecs *codecs,
       
   291     const CActionSet &actionSet, 
       
   292     IInArchive *archive,
       
   293     const CCompressionMethodMode &compressionMethod,
       
   294     CArchivePath &archivePath, 
       
   295     const CObjectVector<CArchiveItem> &archiveItems,
       
   296     bool shareForWrite,
       
   297     bool stdInMode,
       
   298     /* const UString & stdInFileName, */
       
   299     bool stdOutMode,
       
   300     const CObjectVector<CDirItem> &dirItems,
       
   301     bool sfxMode,
       
   302     const UString &sfxModule,
       
   303     const CRecordVector<UInt64> &volumesSizes,
       
   304     CTempFiles &tempFiles,
       
   305     CUpdateErrorInfo &errorInfo,
       
   306     IUpdateCallbackUI *callback)
       
   307 {
       
   308   CMyComPtr<IOutArchive> outArchive;
       
   309   if(archive != NULL)
       
   310   {
       
   311     CMyComPtr<IInArchive> archive2 = archive;
       
   312     HRESULT result = archive2.QueryInterface(IID_IOutArchive, &outArchive);
       
   313     if(result != S_OK)
       
   314       throw kUpdateIsNotSupoorted;
       
   315   }
       
   316   else
       
   317   {
       
   318     RINOK(codecs->CreateOutArchive(compressionMethod.FormatIndex, outArchive));
       
   319 
       
   320     #ifdef EXTERNAL_CODECS
       
   321     {
       
   322       CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo;
       
   323       outArchive.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo);
       
   324       if (setCompressCodecsInfo)
       
   325       {
       
   326         RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(codecs));
       
   327       }
       
   328     }
       
   329     #endif
       
   330   }
       
   331   if (outArchive == 0)
       
   332     throw kUpdateIsNotSupoorted;
       
   333   
       
   334   NFileTimeType::EEnum fileTimeType;
       
   335   UInt32 value;
       
   336   RINOK(outArchive->GetFileTimeType(&value));
       
   337 
       
   338   switch(value)
       
   339   {
       
   340     case NFileTimeType::kWindows:
       
   341     case NFileTimeType::kDOS:
       
   342     case NFileTimeType::kUnix:
       
   343       fileTimeType = NFileTimeType::EEnum(value);
       
   344       break;
       
   345     default:
       
   346       return E_FAIL;
       
   347   }
       
   348 
       
   349   CObjectVector<CUpdatePair> updatePairs;
       
   350   GetUpdatePairInfoList(dirItems, archiveItems, fileTimeType, updatePairs); // must be done only once!!!
       
   351   
       
   352   CObjectVector<CUpdatePair2> updatePairs2;
       
   353   UpdateProduce(updatePairs, actionSet, updatePairs2);
       
   354 
       
   355   UInt32 numFiles = 0;
       
   356   for (int i = 0; i < updatePairs2.Size(); i++)
       
   357     if (updatePairs2[i].NewData)
       
   358       numFiles++;
       
   359   
       
   360   RINOK(callback->SetNumFiles(numFiles));
       
   361 
       
   362   
       
   363   CArchiveUpdateCallback *updateCallbackSpec = new CArchiveUpdateCallback;
       
   364   CMyComPtr<IArchiveUpdateCallback> updateCallback(updateCallbackSpec);
       
   365   
       
   366   updateCallbackSpec->ShareForWrite = shareForWrite;
       
   367   updateCallbackSpec->StdInMode = stdInMode;
       
   368   updateCallbackSpec->Callback = callback;
       
   369   updateCallbackSpec->DirItems = &dirItems;
       
   370   updateCallbackSpec->ArchiveItems = &archiveItems;
       
   371   updateCallbackSpec->UpdatePairs = &updatePairs2;
       
   372 
       
   373   CMyComPtr<ISequentialOutStream> outStream;
       
   374 
       
   375   const UString &archiveName = archivePath.GetFinalPath();
       
   376   if (!stdOutMode)
       
   377   {
       
   378     UString resultPath;
       
   379     int pos;
       
   380     if(!NFile::NDirectory::MyGetFullPathName(archiveName, resultPath, pos))
       
   381       throw 1417161;
       
   382     NFile::NDirectory::CreateComplexDirectory(resultPath.Left(pos));
       
   383   }
       
   384 
       
   385   COutFileStream *outStreamSpec = NULL;
       
   386   COutMultiVolStream *volStreamSpec = NULL;
       
   387 
       
   388   if (volumesSizes.Size() == 0)
       
   389   {
       
   390     if (stdOutMode)
       
   391       outStream = new CStdOutFileStream;
       
   392     else
       
   393     {
       
   394       outStreamSpec = new COutFileStream;
       
   395       outStream = outStreamSpec;
       
   396       bool isOK = false;
       
   397       UString realPath;
       
   398       for (int i = 0; i < (1 << 16); i++)
       
   399       {
       
   400         if (archivePath.Temp)
       
   401         {
       
   402           if (i > 0)
       
   403           {
       
   404             wchar_t s[32];
       
   405             ConvertUInt64ToString(i, s);
       
   406             archivePath.TempPostfix = s;
       
   407           }
       
   408           realPath = archivePath.GetTempPath();
       
   409         }
       
   410         else
       
   411           realPath = archivePath.GetFinalPath();
       
   412         if (outStreamSpec->Create(realPath, false))
       
   413         {
       
   414           tempFiles.Paths.Add(realPath);
       
   415           isOK = true;
       
   416           break;
       
   417         }
       
   418         if (::GetLastError() != ERROR_FILE_EXISTS)
       
   419           break;
       
   420         if (!archivePath.Temp)
       
   421           break;
       
   422       }
       
   423       if (!isOK)
       
   424       {
       
   425         errorInfo.SystemError = ::GetLastError();
       
   426         errorInfo.FileName = realPath;
       
   427         errorInfo.Message = L"Can not open file";
       
   428         return E_FAIL;
       
   429       }
       
   430     }
       
   431   }
       
   432   else
       
   433   {
       
   434     if (stdOutMode)
       
   435       return E_FAIL;
       
   436     volStreamSpec = new COutMultiVolStream;
       
   437     outStream = volStreamSpec;
       
   438     volStreamSpec->Sizes = volumesSizes;
       
   439     volStreamSpec->Prefix = archivePath.GetFinalPath() + UString(L".");
       
   440     volStreamSpec->TempFiles = &tempFiles;
       
   441     volStreamSpec->Init();
       
   442 
       
   443     /*
       
   444     updateCallbackSpec->VolumesSizes = volumesSizes;
       
   445     updateCallbackSpec->VolName = archivePath.Prefix + archivePath.Name;
       
   446     if (!archivePath.VolExtension.IsEmpty())
       
   447       updateCallbackSpec->VolExt = UString(L'.') + archivePath.VolExtension;
       
   448     */
       
   449   }
       
   450 
       
   451   RINOK(SetProperties(outArchive, compressionMethod.Properties));
       
   452 
       
   453   if (sfxMode)
       
   454   {
       
   455     CInFileStream *sfxStreamSpec = new CInFileStream;
       
   456     CMyComPtr<IInStream> sfxStream(sfxStreamSpec);
       
   457     if (!sfxStreamSpec->Open(sfxModule))
       
   458     {
       
   459       errorInfo.SystemError = ::GetLastError();
       
   460       errorInfo.Message = L"Can't open sfx module";
       
   461       errorInfo.FileName = sfxModule;
       
   462       return E_FAIL;
       
   463     }
       
   464 
       
   465     CMyComPtr<ISequentialOutStream> sfxOutStream;
       
   466     COutFileStream *outStreamSpec = NULL;
       
   467     if (volumesSizes.Size() == 0)
       
   468       sfxOutStream = outStream;
       
   469     else
       
   470     {
       
   471       outStreamSpec = new COutFileStream;
       
   472       sfxOutStream = outStreamSpec;
       
   473       UString realPath = archivePath.GetFinalPath();
       
   474       if (!outStreamSpec->Create(realPath, false))
       
   475       {
       
   476         errorInfo.SystemError = ::GetLastError();
       
   477         errorInfo.FileName = realPath;
       
   478         errorInfo.Message = L"Can not open file";
       
   479         return E_FAIL;
       
   480       }
       
   481     }
       
   482     RINOK(CopyBlock(sfxStream, sfxOutStream));
       
   483     if (outStreamSpec)
       
   484     {
       
   485       RINOK(outStreamSpec->Close());
       
   486     }
       
   487   }
       
   488 
       
   489   HRESULT result = outArchive->UpdateItems(outStream, updatePairs2.Size(), updateCallback);
       
   490   callback->Finilize();
       
   491   RINOK(result);
       
   492   if (outStreamSpec)
       
   493     result = outStreamSpec->Close();
       
   494   else if (volStreamSpec)
       
   495     result = volStreamSpec->Close();
       
   496   return result;
       
   497 }
       
   498 
       
   499 HRESULT EnumerateInArchiveItems(const NWildcard::CCensor &censor,
       
   500     IInArchive *archive,
       
   501     const UString &defaultItemName,
       
   502     const NWindows::NFile::NFind::CFileInfoW &archiveFileInfo,
       
   503     CObjectVector<CArchiveItem> &archiveItems)
       
   504 {
       
   505   archiveItems.Clear();
       
   506   UInt32 numItems;
       
   507   RINOK(archive->GetNumberOfItems(&numItems));
       
   508   archiveItems.Reserve(numItems);
       
   509   for(UInt32 i = 0; i < numItems; i++)
       
   510   {
       
   511     CArchiveItem ai;
       
   512 
       
   513     RINOK(GetArchiveItemPath(archive, i, ai.Name));
       
   514     RINOK(IsArchiveItemFolder(archive, i, ai.IsDirectory));
       
   515     ai.Censored = censor.CheckPath(ai.Name.IsEmpty() ? defaultItemName : ai.Name, !ai.IsDirectory);
       
   516     RINOK(GetArchiveItemFileTime(archive, i, 
       
   517         archiveFileInfo.LastWriteTime, ai.LastWriteTime));
       
   518 
       
   519     CPropVariant propertySize;
       
   520     RINOK(archive->GetProperty(i, kpidSize, &propertySize));
       
   521     ai.SizeIsDefined = (propertySize.vt != VT_EMPTY);
       
   522     if (ai.SizeIsDefined)
       
   523       ai.Size = ConvertPropVariantToUInt64(propertySize);
       
   524 
       
   525     ai.IndexInServer = i;
       
   526     archiveItems.Add(ai);
       
   527   }
       
   528   return S_OK;
       
   529 }
       
   530 
       
   531 
       
   532 static HRESULT UpdateWithItemLists(
       
   533     CCodecs *codecs,
       
   534     CUpdateOptions &options,
       
   535     IInArchive *archive, 
       
   536     const CObjectVector<CArchiveItem> &archiveItems,
       
   537     const CObjectVector<CDirItem> &dirItems,
       
   538     CTempFiles &tempFiles,
       
   539     CUpdateErrorInfo &errorInfo,
       
   540     IUpdateCallbackUI2 *callback)
       
   541 {
       
   542   for(int i = 0; i < options.Commands.Size(); i++)
       
   543   {
       
   544     CUpdateArchiveCommand &command = options.Commands[i];
       
   545     if (options.StdOutMode)
       
   546     {
       
   547       RINOK(callback->StartArchive(0, archive != 0));
       
   548     }
       
   549     else
       
   550     {
       
   551       RINOK(callback->StartArchive(command.ArchivePath.GetFinalPath(), 
       
   552           i == 0 && options.UpdateArchiveItself && archive != 0));
       
   553     }
       
   554 
       
   555     RINOK(Compress(
       
   556         codecs,
       
   557         command.ActionSet, archive,
       
   558         options.MethodMode, 
       
   559         command.ArchivePath, 
       
   560         archiveItems, 
       
   561         options.OpenShareForWrite,
       
   562         options.StdInMode, 
       
   563         /* options.StdInFileName, */
       
   564         options.StdOutMode,
       
   565         dirItems, 
       
   566         options.SfxMode, options.SfxModule, 
       
   567         options.VolumesSizes,
       
   568         tempFiles,
       
   569         errorInfo, callback));
       
   570 
       
   571     RINOK(callback->FinishArchive());
       
   572   }
       
   573   return S_OK;
       
   574 }
       
   575 
       
   576 #ifdef _WIN32
       
   577 class CCurrentDirRestorer
       
   578 {
       
   579   UString m_CurrentDirectory;
       
   580 public:
       
   581   CCurrentDirRestorer()
       
   582     { NFile::NDirectory::MyGetCurrentDirectory(m_CurrentDirectory); }
       
   583   ~CCurrentDirRestorer()
       
   584     { RestoreDirectory();}
       
   585   bool RestoreDirectory()
       
   586     { return BOOLToBool(NFile::NDirectory::MySetCurrentDirectory(m_CurrentDirectory)); }
       
   587 };
       
   588 #endif
       
   589 
       
   590 struct CEnumDirItemUpdateCallback: public IEnumDirItemCallback
       
   591 {
       
   592   IUpdateCallbackUI2 *Callback;
       
   593   HRESULT CheckBreak() { return Callback->CheckBreak(); }
       
   594 };
       
   595 
       
   596 HRESULT UpdateArchive(
       
   597     CCodecs *codecs,
       
   598     const NWildcard::CCensor &censor, 
       
   599     CUpdateOptions &options,
       
   600     CUpdateErrorInfo &errorInfo,
       
   601     IOpenCallbackUI *openCallback,
       
   602     IUpdateCallbackUI2 *callback)
       
   603 {
       
   604   if (options.StdOutMode && options.EMailMode)
       
   605     return E_FAIL;
       
   606 
       
   607   if (options.VolumesSizes.Size() > 0 && (options.EMailMode || options.SfxMode))
       
   608     return E_NOTIMPL;
       
   609 
       
   610   if (options.SfxMode)
       
   611   {
       
   612     CProperty property;
       
   613     property.Name = L"rsfx";
       
   614     property.Value = L"on";
       
   615     options.MethodMode.Properties.Add(property);
       
   616     if (options.SfxModule.IsEmpty())
       
   617     {
       
   618       errorInfo.Message = L"sfx file is not specified";
       
   619       return E_FAIL;
       
   620     }
       
   621     UString name = options.SfxModule;
       
   622     if (!NDirectory::MySearchPath(NULL, name, NULL, options.SfxModule))
       
   623     {
       
   624       errorInfo.Message = L"can't find specified sfx module";
       
   625       return E_FAIL;
       
   626     }
       
   627   }
       
   628 
       
   629   const UString archiveName = options.ArchivePath.GetFinalPath();
       
   630 
       
   631   UString defaultItemName;
       
   632   NFind::CFileInfoW archiveFileInfo;
       
   633 
       
   634   CArchiveLink archiveLink;
       
   635   IInArchive *archive = 0;
       
   636   if (NFind::FindFile(archiveName, archiveFileInfo))
       
   637   {
       
   638     if (archiveFileInfo.IsDirectory())
       
   639       throw "there is no such archive";
       
   640     if (options.VolumesSizes.Size() > 0)
       
   641       return E_NOTIMPL;
       
   642     HRESULT result = MyOpenArchive(codecs, archiveName, archiveLink, openCallback);
       
   643     RINOK(callback->OpenResult(archiveName, result));
       
   644     RINOK(result);
       
   645     if (archiveLink.VolumePaths.Size() > 1)
       
   646     {
       
   647       errorInfo.SystemError = (DWORD)E_NOTIMPL;
       
   648       errorInfo.Message = L"Updating for multivolume archives is not implemented";
       
   649       return E_NOTIMPL;
       
   650     }
       
   651     archive = archiveLink.GetArchive();
       
   652     defaultItemName = archiveLink.GetDefaultItemName();
       
   653   }
       
   654   else
       
   655   {
       
   656     /*
       
   657     if (archiveType.IsEmpty())
       
   658       throw "type of archive is not specified";
       
   659     */
       
   660   }
       
   661 
       
   662   CObjectVector<CDirItem> dirItems;
       
   663   if (options.StdInMode)
       
   664   {
       
   665     CDirItem item;
       
   666     item.FullPath = item.Name = options.StdInFileName;
       
   667     item.Size = (UInt64)(Int64)-1;
       
   668     item.Attributes = 0;
       
   669     SYSTEMTIME st;
       
   670     FILETIME ft;
       
   671     GetSystemTime(&st);
       
   672     SystemTimeToFileTime(&st, &ft);
       
   673     item.CreationTime = item.LastAccessTime = item.LastWriteTime = ft;
       
   674     dirItems.Add(item);
       
   675   }
       
   676   else
       
   677   {
       
   678     bool needScanning = false;
       
   679     for(int i = 0; i < options.Commands.Size(); i++)
       
   680       if (options.Commands[i].ActionSet.NeedScanning())
       
   681         needScanning = true;
       
   682     if (needScanning)
       
   683     {
       
   684       CEnumDirItemUpdateCallback enumCallback;
       
   685       enumCallback.Callback = callback;
       
   686       RINOK(callback->StartScanning());
       
   687       UStringVector errorPaths;
       
   688       CRecordVector<DWORD> errorCodes;
       
   689       HRESULT res = EnumerateItems(censor, dirItems, &enumCallback, errorPaths, errorCodes);
       
   690       for (int i = 0; i < errorPaths.Size(); i++)
       
   691       {
       
   692         RINOK(callback->CanNotFindError(errorPaths[i], errorCodes[i]));
       
   693       }
       
   694       if(res != S_OK) 
       
   695       {
       
   696         errorInfo.Message = L"Scanning error";
       
   697         // errorInfo.FileName = errorPath;
       
   698         return res;
       
   699       }
       
   700       RINOK(callback->FinishScanning());
       
   701     }
       
   702   }
       
   703 
       
   704   UString tempDirPrefix;
       
   705   bool usesTempDir = false;
       
   706   
       
   707   #ifdef _WIN32
       
   708   NDirectory::CTempDirectoryW tempDirectory;
       
   709   if (options.EMailMode && options.EMailRemoveAfter)
       
   710   {
       
   711     tempDirectory.Create(kTempFolderPrefix);
       
   712     tempDirPrefix = tempDirectory.GetPath();
       
   713     NormalizeDirPathPrefix(tempDirPrefix);
       
   714     usesTempDir = true;
       
   715   }
       
   716   #endif
       
   717 
       
   718   CTempFiles tempFiles;
       
   719 
       
   720   bool createTempFile = false;
       
   721   if(!options.StdOutMode && options.UpdateArchiveItself)
       
   722   {
       
   723     CArchivePath &ap = options.Commands[0].ArchivePath;
       
   724     ap = options.ArchivePath;
       
   725     // if ((archive != 0 && !usesTempDir) || !options.WorkingDir.IsEmpty())
       
   726     if ((archive != 0 || !options.WorkingDir.IsEmpty()) && !usesTempDir && options.VolumesSizes.Size() == 0)
       
   727     {
       
   728       createTempFile = true;
       
   729       ap.Temp = true;
       
   730       if (!options.WorkingDir.IsEmpty())
       
   731       {
       
   732         ap.TempPrefix = options.WorkingDir;
       
   733         NormalizeDirPathPrefix(ap.TempPrefix);
       
   734       }
       
   735     }
       
   736   }
       
   737 
       
   738   for(int i = 0; i < options.Commands.Size(); i++)
       
   739   {
       
   740     CArchivePath &ap = options.Commands[i].ArchivePath;
       
   741     if (usesTempDir)
       
   742     {
       
   743       // Check it
       
   744       ap.Prefix = tempDirPrefix;
       
   745       // ap.Temp = true;
       
   746       // ap.TempPrefix = tempDirPrefix;
       
   747     }
       
   748     if (i > 0 || !createTempFile)
       
   749     {
       
   750       const UString &path = ap.GetFinalPath();
       
   751       if (NFind::DoesFileExist(path))
       
   752       {
       
   753         errorInfo.SystemError = 0;
       
   754         errorInfo.Message = L"File already exists";
       
   755         errorInfo.FileName = path;
       
   756         return E_FAIL;
       
   757       }
       
   758     }
       
   759   }
       
   760 
       
   761   CObjectVector<CArchiveItem> archiveItems;
       
   762   if (archive != NULL)
       
   763   {
       
   764     RINOK(EnumerateInArchiveItems(censor, 
       
   765         archive, defaultItemName, archiveFileInfo, archiveItems));
       
   766   }
       
   767 
       
   768   RINOK(UpdateWithItemLists(codecs, options, archive, archiveItems, dirItems, 
       
   769       tempFiles, errorInfo, callback));
       
   770 
       
   771   if (archive != NULL)
       
   772   {
       
   773     RINOK(archiveLink.Close());
       
   774     archiveLink.Release();
       
   775   }
       
   776 
       
   777   tempFiles.Paths.Clear();
       
   778   if(createTempFile)
       
   779   {
       
   780     try
       
   781     {
       
   782       CArchivePath &ap = options.Commands[0].ArchivePath;
       
   783       const UString &tempPath = ap.GetTempPath();
       
   784       if (archive != NULL)
       
   785         if (!NDirectory::DeleteFileAlways(archiveName))
       
   786         {
       
   787           errorInfo.SystemError = ::GetLastError();
       
   788           errorInfo.Message = L"delete file error";
       
   789           errorInfo.FileName = archiveName;
       
   790           return E_FAIL;
       
   791         }
       
   792       if (!NDirectory::MyMoveFile(tempPath, archiveName))
       
   793       {
       
   794         errorInfo.SystemError = ::GetLastError();
       
   795         errorInfo.Message = L"move file error";
       
   796         errorInfo.FileName = tempPath;
       
   797         errorInfo.FileName2 = archiveName;
       
   798         return E_FAIL;
       
   799       }
       
   800     }
       
   801     catch(...)
       
   802     {
       
   803       throw;
       
   804     }
       
   805   }
       
   806 
       
   807   #ifdef _WIN32
       
   808   if (options.EMailMode)
       
   809   {
       
   810     NDLL::CLibrary mapiLib;
       
   811     if (!mapiLib.Load(TEXT("Mapi32.dll")))
       
   812     {
       
   813       errorInfo.SystemError = ::GetLastError();
       
   814       errorInfo.Message = L"can not load Mapi32.dll";
       
   815       return E_FAIL;
       
   816     }
       
   817     LPMAPISENDDOCUMENTS fnSend = (LPMAPISENDDOCUMENTS)
       
   818         mapiLib.GetProcAddress("MAPISendDocuments");
       
   819     if (fnSend == 0)
       
   820     {
       
   821       errorInfo.SystemError = ::GetLastError();
       
   822       errorInfo.Message = L"can not find MAPISendDocuments function";
       
   823       return E_FAIL;
       
   824     }
       
   825     UStringVector fullPaths;
       
   826     int i;
       
   827     for(i = 0; i < options.Commands.Size(); i++)
       
   828     {
       
   829       CArchivePath &ap = options.Commands[i].ArchivePath;
       
   830       UString arcPath;
       
   831       if(!NFile::NDirectory::MyGetFullPathName(ap.GetFinalPath(), arcPath))
       
   832       {
       
   833         errorInfo.SystemError = ::GetLastError();
       
   834         return E_FAIL;
       
   835       }
       
   836       fullPaths.Add(arcPath);
       
   837     }
       
   838     CCurrentDirRestorer curDirRestorer;
       
   839     for(i = 0; i < fullPaths.Size(); i++)
       
   840     {
       
   841       UString arcPath = fullPaths[i];
       
   842       UString fileName = ExtractFileNameFromPath(arcPath);
       
   843       AString path = GetAnsiString(arcPath);
       
   844       AString name = GetAnsiString(fileName);
       
   845       // Warning!!! MAPISendDocuments function changes Current directory
       
   846       fnSend(0, ";", (LPSTR)(LPCSTR)path, (LPSTR)(LPCSTR)name, 0); 
       
   847     }
       
   848   }
       
   849   #endif
       
   850   return S_OK;
       
   851 }
       
   852