"Fossies" - the Fresh Open Source Software Archive

Member "unrar/extract.cpp" (4 May 2022, 41862 Bytes) of package /linux/misc/unrarsrc-6.1.7.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "extract.cpp" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 6.1.4_vs_6.1.5.

    1 #include "rar.hpp"
    2 
    3 CmdExtract::CmdExtract(CommandData *Cmd)
    4 {
    5   CmdExtract::Cmd=Cmd;
    6 
    7   *ArcName=0;
    8 
    9   *DestFileName=0;
   10 
   11   TotalFileCount=0;
   12   Unp=new Unpack(&DataIO);
   13 #ifdef RAR_SMP
   14   Unp->SetThreads(Cmd->Threads);
   15 #endif
   16 }
   17 
   18 
   19 CmdExtract::~CmdExtract()
   20 {
   21   delete Unp;
   22 }
   23 
   24 
   25 void CmdExtract::DoExtract()
   26 {
   27 #if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT)
   28   Fat32=NotFat32=false;
   29 #endif
   30   PasswordCancelled=false;
   31   DataIO.SetCurrentCommand(Cmd->Command[0]);
   32 
   33   FindData FD;
   34   while (Cmd->GetArcName(ArcName,ASIZE(ArcName)))
   35     if (FindFile::FastFind(ArcName,&FD))
   36       DataIO.TotalArcSize+=FD.Size;
   37 
   38   Cmd->ArcNames.Rewind();
   39   while (Cmd->GetArcName(ArcName,ASIZE(ArcName)))
   40   {
   41     if (Cmd->ManualPassword)
   42       Cmd->Password.Clean(); // Clean user entered password before processing next archive.
   43   
   44     ReconstructDone=false; // Must be reset here, not in ExtractArchiveInit().
   45     UseExactVolName=false; // Must be reset here, not in ExtractArchiveInit().
   46     while (true)
   47     {
   48       EXTRACT_ARC_CODE Code=ExtractArchive();
   49       if (Code!=EXTRACT_ARC_REPEAT)
   50         break;
   51     }
   52     DataIO.ProcessedArcSize+=DataIO.LastArcSize;
   53   }
   54 
   55   // Clean user entered password. Not really required, just for extra safety.
   56   if (Cmd->ManualPassword)
   57     Cmd->Password.Clean();
   58 
   59   if (TotalFileCount==0 && Cmd->Command[0]!='I' && 
   60       ErrHandler.GetErrorCode()!=RARX_BADPWD) // Not in case of wrong archive password.
   61   {
   62     if (!PasswordCancelled)
   63       uiMsg(UIERROR_NOFILESTOEXTRACT,ArcName);
   64 
   65     // Other error codes may explain a reason of "no files extracted" clearer,
   66     // so set it only if no other errors found (wrong mask set by user).
   67     if (ErrHandler.GetErrorCode()==RARX_SUCCESS)
   68       ErrHandler.SetErrorCode(RARX_NOFILES);
   69   }
   70   else
   71     if (!Cmd->DisableDone)
   72       if (Cmd->Command[0]=='I')
   73         mprintf(St(MDone));
   74       else
   75         if (ErrHandler.GetErrorCount()==0)
   76           mprintf(St(MExtrAllOk));
   77         else
   78           mprintf(St(MExtrTotalErr),ErrHandler.GetErrorCount());
   79 }
   80 
   81 
   82 void CmdExtract::ExtractArchiveInit(Archive &Arc)
   83 {
   84   DataIO.AdjustTotalArcSize(&Arc);
   85 
   86   FileCount=0;
   87   MatchedArgs=0;
   88 #ifndef SFX_MODULE
   89   FirstFile=true;
   90 #endif
   91 
   92   GlobalPassword=Cmd->Password.IsSet() || uiIsGlobalPasswordSet();
   93 
   94   DataIO.UnpVolume=false;
   95 
   96   PrevProcessed=false;
   97   AllMatchesExact=true;
   98   AnySolidDataUnpackedWell=false;
   99 
  100   StartTime.SetCurrentTime();
  101 }
  102 
  103 
  104 EXTRACT_ARC_CODE CmdExtract::ExtractArchive()
  105 {
  106   Archive Arc(Cmd);
  107   if (*Cmd->UseStdin!=0)
  108   {
  109     Arc.SetHandleType(FILE_HANDLESTD);
  110 #ifdef USE_QOPEN
  111     Arc.SetProhibitQOpen(true);
  112 #endif
  113   }
  114   else
  115   {
  116 #if defined(_WIN_ALL) && !defined(SFX_MODULE) // WinRAR GUI code also resets the cache.
  117     if (*Cmd->Command=='T' || Cmd->Test)
  118       ResetFileCache(ArcName); // Reset the file cache when testing an archive.
  119 #endif
  120     if (!Arc.WOpen(ArcName))
  121       return EXTRACT_ARC_NEXT;
  122   }
  123 
  124   if (!Arc.IsArchive(true))
  125   {
  126 #if !defined(SFX_MODULE) && !defined(RARDLL)
  127     if (CmpExt(ArcName,L"rev"))
  128     {
  129       wchar FirstVolName[NM];
  130       VolNameToFirstName(ArcName,FirstVolName,ASIZE(FirstVolName),true);
  131 
  132       // If several volume names from same volume set are specified
  133       // and current volume is not first in set and first volume is present
  134       // and specified too, let's skip the current volume.
  135       if (wcsicomp(ArcName,FirstVolName)!=0 && FileExist(FirstVolName) &&
  136           Cmd->ArcNames.Search(FirstVolName,false))
  137         return EXTRACT_ARC_NEXT;
  138       RecVolumesTest(Cmd,NULL,ArcName);
  139       TotalFileCount++; // Suppress "No files to extract" message.
  140       return EXTRACT_ARC_NEXT;
  141     }
  142 #endif
  143 
  144     mprintf(St(MNotRAR),ArcName);
  145 
  146 #ifndef SFX_MODULE
  147     if (CmpExt(ArcName,L"rar"))
  148 #endif
  149       ErrHandler.SetErrorCode(RARX_WARNING);
  150     return EXTRACT_ARC_NEXT;
  151   }
  152 
  153   if (Arc.FailedHeaderDecryption) // Bad archive password.
  154     return EXTRACT_ARC_NEXT;
  155 
  156 #ifndef SFX_MODULE
  157   if (Arc.Volume && !Arc.FirstVolume && !UseExactVolName)
  158   {
  159     wchar FirstVolName[NM];
  160     VolNameToFirstName(ArcName,FirstVolName,ASIZE(FirstVolName),Arc.NewNumbering);
  161 
  162     // If several volume names from same volume set are specified
  163     // and current volume is not first in set and first volume is present
  164     // and specified too, let's skip the current volume.
  165     if (wcsicomp(ArcName,FirstVolName)!=0 && FileExist(FirstVolName) &&
  166         Cmd->ArcNames.Search(FirstVolName,false))
  167       return EXTRACT_ARC_NEXT;
  168   }
  169 #endif
  170 
  171   int64 VolumeSetSize=0; // Total size of volumes after the current volume.
  172 
  173   if (Arc.Volume)
  174   {
  175 #ifndef SFX_MODULE
  176     // Try to speed up extraction for independent solid volumes by starting
  177     // extraction from non-first volume if we can.
  178     if (!UseExactVolName && Arc.Solid && DetectStartVolume(Arc.FileName,Arc.NewNumbering))
  179     {
  180       UseExactVolName=true;
  181       return EXTRACT_ARC_REPEAT;
  182     }
  183 #endif
  184 
  185     // Calculate the total size of all accessible volumes.
  186     // This size is necessary to display the correct total progress indicator.
  187 
  188     wchar NextName[NM];
  189     wcsncpyz(NextName,Arc.FileName,ASIZE(NextName));
  190 
  191     while (true)
  192     {
  193       // First volume is already added to DataIO.TotalArcSize 
  194       // in initial TotalArcSize calculation in DoExtract.
  195       // So we skip it and start from second volume.
  196       NextVolumeName(NextName,ASIZE(NextName),!Arc.NewNumbering);
  197       FindData FD;
  198       if (FindFile::FastFind(NextName,&FD))
  199         VolumeSetSize+=FD.Size;
  200       else
  201         break;
  202     }
  203     DataIO.TotalArcSize+=VolumeSetSize;
  204   }
  205 
  206   ExtractArchiveInit(Arc);
  207 
  208   if (*Cmd->Command=='T' || *Cmd->Command=='I')
  209     Cmd->Test=true;
  210 
  211 
  212   if (*Cmd->Command=='I')
  213   {
  214     Cmd->DisablePercentage=true;
  215   }
  216   else
  217     uiStartArchiveExtract(!Cmd->Test,ArcName);
  218 
  219   Arc.ViewComment();
  220 
  221 
  222   while (1)
  223   {
  224     size_t Size=Arc.ReadHeader();
  225 
  226 
  227     bool Repeat=false;
  228     if (!ExtractCurrentFile(Arc,Size,Repeat))
  229       if (Repeat)
  230       {
  231         // If we started extraction from not first volume and need to
  232         // restart it from first, we must set DataIO.TotalArcSize to size
  233         // of new first volume to display the total progress correctly.
  234         FindData NewArc;
  235         if (FindFile::FastFind(ArcName,&NewArc))
  236           DataIO.TotalArcSize=NewArc.Size;
  237         return EXTRACT_ARC_REPEAT;
  238       }
  239       else
  240         break;
  241   }
  242 
  243 
  244 #if !defined(SFX_MODULE) && !defined(RARDLL)
  245   if (Cmd->Test && Arc.Volume)
  246     RecVolumesTest(Cmd,&Arc,ArcName);
  247 #endif
  248 
  249   return EXTRACT_ARC_NEXT;
  250 }
  251 
  252 
  253 bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
  254 {
  255   wchar Command=Cmd->Command[0];
  256   if (HeaderSize==0)
  257     if (DataIO.UnpVolume)
  258     {
  259 #ifdef NOVOLUME
  260       return false;
  261 #else
  262       // Supposing we unpack an old RAR volume without the end of archive
  263       // record and last file is not split between volumes.
  264       if (!MergeArchive(Arc,&DataIO,false,Command))
  265       {
  266         ErrHandler.SetErrorCode(RARX_WARNING);
  267         return false;
  268       }
  269 #endif
  270     }
  271     else
  272       return false;
  273 
  274   HEADER_TYPE HeaderType=Arc.GetHeaderType();
  275   if (HeaderType!=HEAD_FILE)
  276   {
  277 #ifndef SFX_MODULE
  278     if (Arc.Format==RARFMT15 && HeaderType==HEAD3_OLDSERVICE && PrevProcessed)
  279       SetExtraInfo20(Cmd,Arc,DestFileName);
  280 #endif
  281     if (HeaderType==HEAD_SERVICE && PrevProcessed)
  282       SetExtraInfo(Cmd,Arc,DestFileName);
  283     if (HeaderType==HEAD_ENDARC)
  284       if (Arc.EndArcHead.NextVolume)
  285       {
  286 #ifdef NOVOLUME
  287         return false;
  288 #else
  289         if (!MergeArchive(Arc,&DataIO,false,Command))
  290         {
  291           ErrHandler.SetErrorCode(RARX_WARNING);
  292           return false;
  293         }
  294         Arc.Seek(Arc.CurBlockPos,SEEK_SET);
  295         return true;
  296 #endif
  297       }
  298       else
  299         return false;
  300     Arc.SeekToNext();
  301     return true;
  302   }
  303   PrevProcessed=false;
  304 
  305   // We can get negative sizes in corrupt archive and it is unacceptable
  306   // for size comparisons in ComprDataIO::UnpRead, where we cast sizes
  307   // to size_t and can exceed another read or available size. We could fix it
  308   // when reading an archive. But we prefer to do it here, because this
  309   // function is called directly in unrar.dll, so we fix bad parameters
  310   // passed to dll. Also we want to see real negative sizes in the listing
  311   // of corrupt archive. To prevent uninitialized data access perform
  312   // these checks after rejecting zero length and non-file headers above.
  313   if (Arc.FileHead.PackSize<0)
  314     Arc.FileHead.PackSize=0;
  315   if (Arc.FileHead.UnpSize<0)
  316     Arc.FileHead.UnpSize=0;
  317 
  318   if (!Cmd->Recurse && MatchedArgs>=Cmd->FileArgs.ItemsCount() && AllMatchesExact)
  319     return false;
  320 
  321   int MatchType=MATCH_WILDSUBPATH;
  322 
  323   bool EqualNames=false;
  324   wchar MatchedArg[NM];
  325   int MatchNumber=Cmd->IsProcessFile(Arc.FileHead,&EqualNames,MatchType,0,MatchedArg,ASIZE(MatchedArg));
  326   bool MatchFound=MatchNumber!=0;
  327 #ifndef SFX_MODULE
  328   if (Cmd->ExclPath==EXCL_BASEPATH)
  329   {
  330     wcsncpyz(Cmd->ArcPath,MatchedArg,ASIZE(Cmd->ArcPath));
  331     *PointToName(Cmd->ArcPath)=0;
  332     if (IsWildcard(Cmd->ArcPath)) // Cannot correctly process path*\* masks here.
  333       *Cmd->ArcPath=0;
  334   }
  335 #endif
  336   if (MatchFound && !EqualNames)
  337     AllMatchesExact=false;
  338 
  339   Arc.ConvertAttributes();
  340 
  341 #if !defined(SFX_MODULE) && !defined(RARDLL)
  342   if (Arc.FileHead.SplitBefore && FirstFile && !UseExactVolName)
  343   {
  344     wchar CurVolName[NM];
  345     wcsncpyz(CurVolName,ArcName,ASIZE(CurVolName));
  346     GetFirstVolIfFullSet(ArcName,Arc.NewNumbering,ArcName,ASIZE(ArcName));
  347 
  348     if (wcsicomp(ArcName,CurVolName)!=0 && FileExist(ArcName))
  349     {
  350       wcsncpyz(Cmd->ArcName,ArcName,ASIZE(ArcName)); // For GUI "Delete archive after extraction".
  351       // If first volume name does not match the current name and if such
  352       // volume name really exists, let's unpack from this first volume.
  353       Repeat=true;
  354       return false;
  355     }
  356 #ifndef RARDLL
  357     if (!ReconstructDone)
  358     {
  359       ReconstructDone=true;
  360       if (RecVolumesRestore(Cmd,Arc.FileName,true))
  361       {
  362         Repeat=true;
  363         return false;
  364       }
  365     }
  366 #endif
  367     wcsncpyz(ArcName,CurVolName,ASIZE(ArcName));
  368   }
  369 #endif
  370 
  371   wchar ArcFileName[NM];
  372   ConvertPath(Arc.FileHead.FileName,ArcFileName,ASIZE(ArcFileName));
  373 
  374   if (Arc.FileHead.Version)
  375   {
  376     if (Cmd->VersionControl!=1 && !EqualNames)
  377     {
  378       if (Cmd->VersionControl==0)
  379         MatchFound=false;
  380       int Version=ParseVersionFileName(ArcFileName,false);
  381       if (Cmd->VersionControl-1==Version)
  382         ParseVersionFileName(ArcFileName,true);
  383       else
  384         MatchFound=false;
  385     }
  386   }
  387   else
  388     if (!Arc.IsArcDir() && Cmd->VersionControl>1)
  389       MatchFound=false;
  390 
  391   DataIO.UnpVolume=Arc.FileHead.SplitAfter;
  392   DataIO.NextVolumeMissing=false;
  393 
  394   Arc.Seek(Arc.NextBlockPos-Arc.FileHead.PackSize,SEEK_SET);
  395 
  396   bool ExtrFile=false;
  397   bool SkipSolid=false;
  398 
  399 #ifndef SFX_MODULE
  400   if (FirstFile && (MatchFound || Arc.Solid) && Arc.FileHead.SplitBefore)
  401   {
  402     if (MatchFound)
  403     {
  404       uiMsg(UIERROR_NEEDPREVVOL,Arc.FileName,ArcFileName);
  405 #ifdef RARDLL
  406       Cmd->DllError=ERAR_BAD_DATA;
  407 #endif
  408       ErrHandler.SetErrorCode(RARX_OPEN);
  409     }
  410     MatchFound=false;
  411   }
  412 
  413   FirstFile=false;
  414 #endif
  415 
  416   if (Arc.FileHead.Encrypted && Cmd->SkipEncrypted)
  417     if (Arc.Solid)
  418       return false; // Abort the entire extraction for solid archive.
  419     else
  420       MatchFound=false; // Skip only the current file for non-solid archive.
  421   
  422   if (MatchFound || (SkipSolid=Arc.Solid)!=0)
  423   {
  424     // First common call of uiStartFileExtract. It is done before overwrite
  425     // prompts, so if SkipSolid state is changed below, we'll need to make
  426     // additional uiStartFileExtract calls with updated parameters.
  427     if (!uiStartFileExtract(ArcFileName,!Cmd->Test,Cmd->Test && Command!='I',SkipSolid))
  428       return false;
  429 
  430     ExtrPrepareName(Arc,ArcFileName,DestFileName,ASIZE(DestFileName));
  431 
  432     // DestFileName can be set empty in case of excessive -ap switch.
  433     ExtrFile=!SkipSolid && *DestFileName!=0 && !Arc.FileHead.SplitBefore;
  434 
  435     if ((Cmd->FreshFiles || Cmd->UpdateFiles) && (Command=='E' || Command=='X'))
  436     {
  437       FindData FD;
  438       if (FindFile::FastFind(DestFileName,&FD))
  439       {
  440         if (FD.mtime >= Arc.FileHead.mtime)
  441         {
  442           // If directory already exists and its modification time is newer 
  443           // than start of extraction, it is likely it was created 
  444           // when creating a path to one of already extracted items. 
  445           // In such case we'll better update its time even if archived 
  446           // directory is older.
  447 
  448           if (!FD.IsDir || FD.mtime<StartTime)
  449             ExtrFile=false;
  450         }
  451       }
  452       else
  453         if (Cmd->FreshFiles)
  454           ExtrFile=false;
  455     }
  456 
  457     if (!CheckUnpVer(Arc,ArcFileName))
  458     {
  459       ErrHandler.SetErrorCode(RARX_FATAL);
  460 #ifdef RARDLL
  461       Cmd->DllError=ERAR_UNKNOWN_FORMAT;
  462 #endif
  463       Arc.SeekToNext();
  464       return !Arc.Solid; // Can try extracting next file only in non-solid archive.
  465     }
  466 
  467     while (true) // Repeat the password prompt for wrong and empty passwords.
  468     {
  469       if (Arc.FileHead.Encrypted)
  470       {
  471         // Stop archive extracting if user cancelled a password prompt.
  472 #ifdef RARDLL
  473         if (!ExtrDllGetPassword())
  474         {
  475           Cmd->DllError=ERAR_MISSING_PASSWORD;
  476           return false;
  477         }
  478 #else
  479         if (!ExtrGetPassword(Arc,ArcFileName))
  480         {
  481           PasswordCancelled=true;
  482           return false;
  483         }
  484 #endif
  485       }
  486 
  487       // Set a password before creating the file, so we can skip creating
  488       // in case of wrong password.
  489       SecPassword FilePassword=Cmd->Password;
  490 #if defined(_WIN_ALL) && !defined(SFX_MODULE)
  491       ConvertDosPassword(Arc,FilePassword);
  492 #endif
  493 
  494       byte PswCheck[SIZE_PSWCHECK];
  495       DataIO.SetEncryption(false,Arc.FileHead.CryptMethod,&FilePassword,
  496              Arc.FileHead.SaltSet ? Arc.FileHead.Salt:NULL,
  497              Arc.FileHead.InitV,Arc.FileHead.Lg2Count,
  498              Arc.FileHead.HashKey,PswCheck);
  499 
  500       // If header is damaged, we cannot rely on password check value,
  501       // because it can be damaged too.
  502       if (Arc.FileHead.Encrypted && Arc.FileHead.UsePswCheck &&
  503           memcmp(Arc.FileHead.PswCheck,PswCheck,SIZE_PSWCHECK)!=0 &&
  504           !Arc.BrokenHeader)
  505       {
  506         if (GlobalPassword) // For -p<pwd> or Ctrl+P to avoid the infinite loop.
  507         {
  508           // This message is used by Android GUI to reset cached passwords.
  509           // Update appropriate code if changed.
  510           uiMsg(UIERROR_BADPSW,Arc.FileName,ArcFileName);
  511         }
  512         else // For passwords entered manually.
  513         {
  514           // This message is used by Android GUI and Windows GUI and SFX to
  515           // reset cached passwords. Update appropriate code if changed.
  516           uiMsg(UIWAIT_BADPSW,Arc.FileName,ArcFileName);
  517           Cmd->Password.Clean();
  518 
  519           // Avoid new requests for unrar.dll to prevent the infinite loop
  520           // if app always returns the same password.
  521 #ifndef RARDLL
  522           continue; // Request a password again.
  523 #endif
  524         }
  525 #ifdef RARDLL
  526         // If we already have ERAR_EOPEN as result of missing volume,
  527         // we should not replace it with less precise ERAR_BAD_PASSWORD.
  528         if (Cmd->DllError!=ERAR_EOPEN)
  529           Cmd->DllError=ERAR_BAD_PASSWORD;
  530 #endif
  531         ErrHandler.SetErrorCode(RARX_BADPWD);
  532         ExtrFile=false;
  533       }
  534       break;
  535     }
  536 
  537 #ifdef RARDLL
  538     if (*Cmd->DllDestName!=0)
  539       wcsncpyz(DestFileName,Cmd->DllDestName,ASIZE(DestFileName));
  540 #endif
  541 
  542     File CurFile;
  543 
  544     bool LinkEntry=Arc.FileHead.RedirType!=FSREDIR_NONE;
  545     if (LinkEntry && Arc.FileHead.RedirType!=FSREDIR_FILECOPY)
  546     {
  547       if (ExtrFile && Command!='P' && !Cmd->Test)
  548       {
  549         // Overwrite prompt for symbolic and hard links.
  550         bool UserReject=false;
  551         if (FileExist(DestFileName) && !UserReject)
  552           FileCreate(Cmd,NULL,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime);
  553         if (UserReject)
  554           ExtrFile=false;
  555       }
  556     }
  557     else
  558       if (Arc.IsArcDir())
  559       {
  560         if (!ExtrFile || Command=='P' || Command=='I' || Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH)
  561           return true;
  562         TotalFileCount++;
  563         ExtrCreateDir(Arc,ArcFileName);
  564         // It is important to not increment MatchedArgs here, so we extract
  565         // dir with its entire contents and not dir record only even if
  566         // dir record precedes files.
  567         return true;
  568       }
  569       else
  570         if (ExtrFile) // Create files and file copies (FSREDIR_FILECOPY).
  571           ExtrFile=ExtrCreateFile(Arc,CurFile);
  572 
  573     if (!ExtrFile && Arc.Solid)
  574     {
  575       SkipSolid=true;
  576       ExtrFile=true;
  577 
  578       // We changed SkipSolid, so we need to call uiStartFileExtract
  579       // with "Skip" parameter to change the operation status 
  580       // from "extracting" to "skipping". For example, it can be necessary
  581       // if user answered "No" to overwrite prompt when unpacking
  582       // a solid archive.
  583       if (!uiStartFileExtract(ArcFileName,false,false,true))
  584         return false;
  585     }
  586     if (ExtrFile)
  587     {
  588       // Set it in test mode, so we also test subheaders such as NTFS streams
  589       // after tested file.
  590       if (Cmd->Test)
  591         PrevProcessed=true;
  592 
  593       bool TestMode=Cmd->Test || SkipSolid; // Unpack to memory, not to disk.
  594 
  595       if (!SkipSolid)
  596       {
  597         if (!TestMode && Command!='P' && CurFile.IsDevice())
  598         {
  599           uiMsg(UIERROR_INVALIDNAME,Arc.FileName,DestFileName);
  600           ErrHandler.WriteError(Arc.FileName,DestFileName);
  601         }
  602         TotalFileCount++;
  603       }
  604       FileCount++;
  605       if (Command!='I' && !Cmd->DisableNames)
  606         if (SkipSolid)
  607           mprintf(St(MExtrSkipFile),ArcFileName);
  608         else
  609           switch(Cmd->Test ? 'T':Command) // "Test" can be also enabled by -t switch.
  610           {
  611             case 'T':
  612               mprintf(St(MExtrTestFile),ArcFileName);
  613               break;
  614 #ifndef SFX_MODULE
  615             case 'P':
  616               mprintf(St(MExtrPrinting),ArcFileName);
  617               break;
  618 #endif
  619             case 'X':
  620             case 'E':
  621               mprintf(St(MExtrFile),DestFileName);
  622               break;
  623           }
  624       if (!Cmd->DisablePercentage && !Cmd->DisableNames)
  625         mprintf(L"     ");
  626       if (Cmd->DisableNames)
  627         uiEolAfterMsg(); // Avoid erasing preceding messages by percentage indicator in -idn mode.
  628 
  629       DataIO.CurUnpRead=0;
  630       DataIO.CurUnpWrite=0;
  631       DataIO.UnpHash.Init(Arc.FileHead.FileHash.Type,Cmd->Threads);
  632       DataIO.PackedDataHash.Init(Arc.FileHead.FileHash.Type,Cmd->Threads);
  633       DataIO.SetPackedSizeToRead(Arc.FileHead.PackSize);
  634       DataIO.SetFiles(&Arc,&CurFile);
  635       DataIO.SetTestMode(TestMode);
  636       DataIO.SetSkipUnpCRC(SkipSolid);
  637 
  638 #if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT)
  639       if (!TestMode && !Arc.BrokenHeader &&
  640           Arc.FileHead.UnpSize>0xffffffff && (Fat32 || !NotFat32))
  641       {
  642         if (!Fat32) // Not detected yet.
  643           NotFat32=!(Fat32=IsFAT(Cmd->ExtrPath));
  644         if (Fat32)
  645           uiMsg(UIMSG_FAT32SIZE); // Inform user about FAT32 size limit.
  646       }
  647 #endif
  648 
  649       uint64 Preallocated=0;
  650       if (!TestMode && !Arc.BrokenHeader && Arc.FileHead.UnpSize>1000000 &&
  651           Arc.FileHead.PackSize*1024>Arc.FileHead.UnpSize && Arc.IsSeekable() &&
  652           (Arc.FileHead.UnpSize<100000000 || Arc.FileLength()>Arc.FileHead.PackSize))
  653       {
  654         CurFile.Prealloc(Arc.FileHead.UnpSize);
  655         Preallocated=Arc.FileHead.UnpSize;
  656       }
  657       CurFile.SetAllowDelete(!Cmd->KeepBroken);
  658 
  659       bool FileCreateMode=!TestMode && !SkipSolid && Command!='P';
  660       bool ShowChecksum=true; // Display checksum verification result.
  661 
  662       bool LinkSuccess=true; // Assume success for test mode.
  663       if (LinkEntry)
  664       {
  665         FILE_SYSTEM_REDIRECT Type=Arc.FileHead.RedirType;
  666 
  667         if (Type==FSREDIR_HARDLINK || Type==FSREDIR_FILECOPY)
  668         {
  669           wchar RedirName[NM];
  670           ConvertPath(Arc.FileHead.RedirName,RedirName,ASIZE(RedirName));
  671 
  672           wchar NameExisting[NM];
  673           ExtrPrepareName(Arc,RedirName,NameExisting,ASIZE(NameExisting));
  674           if (FileCreateMode && *NameExisting!=0) // *NameExisting can be 0 in case of excessive -ap switch.
  675             if (Type==FSREDIR_HARDLINK)
  676               LinkSuccess=ExtractHardlink(Cmd,DestFileName,NameExisting,ASIZE(NameExisting));
  677             else
  678               LinkSuccess=ExtractFileCopy(CurFile,Arc.FileName,DestFileName,NameExisting,ASIZE(NameExisting));
  679         }
  680         else
  681           if (Type==FSREDIR_UNIXSYMLINK || Type==FSREDIR_WINSYMLINK || Type==FSREDIR_JUNCTION)
  682           {
  683             if (FileCreateMode)
  684               LinkSuccess=ExtractSymlink(Cmd,DataIO,Arc,DestFileName);
  685           }
  686           else
  687           {
  688             uiMsg(UIERROR_UNKNOWNEXTRA,Arc.FileName,DestFileName);
  689             LinkSuccess=false;
  690           }
  691           
  692           if (!LinkSuccess || Arc.Format==RARFMT15 && !FileCreateMode)
  693           {
  694             // RAR 5.x links have a valid data checksum even in case of
  695             // failure, because they do not store any data.
  696             // We do not want to display "OK" in this case.
  697             // For 4.x symlinks we verify the checksum only when extracting,
  698             // but not when testing an archive.
  699             ShowChecksum=false;
  700           }
  701           PrevProcessed=FileCreateMode && LinkSuccess;
  702       }
  703       else
  704         if (!Arc.FileHead.SplitBefore)
  705           if (Arc.FileHead.Method==0)
  706             UnstoreFile(DataIO,Arc.FileHead.UnpSize);
  707           else
  708           {
  709             Unp->Init(Arc.FileHead.WinSize,Arc.FileHead.Solid);
  710             Unp->SetDestSize(Arc.FileHead.UnpSize);
  711 #ifndef SFX_MODULE
  712             if (Arc.Format!=RARFMT50 && Arc.FileHead.UnpVer<=15)
  713               Unp->DoUnpack(15,FileCount>1 && Arc.Solid);
  714             else
  715 #endif
  716               Unp->DoUnpack(Arc.FileHead.UnpVer,Arc.FileHead.Solid);
  717           }
  718 
  719       Arc.SeekToNext();
  720 
  721       // We check for "split after" flag to detect partially extracted files
  722       // from incomplete volume sets. For them file header contains packed
  723       // data hash, which must not be compared against unpacked data hash
  724       // to prevent accidental match. Moreover, for -m0 volumes packed data
  725       // hash would match truncated unpacked data hash and lead to fake "OK"
  726       // in incomplete volume set.
  727       bool ValidCRC=!Arc.FileHead.SplitAfter && DataIO.UnpHash.Cmp(&Arc.FileHead.FileHash,Arc.FileHead.UseHashKey ? Arc.FileHead.HashKey:NULL);
  728 
  729       // We set AnySolidDataUnpackedWell to true if we found at least one
  730       // valid non-zero solid file in preceding solid stream. If it is true
  731       // and if current encrypted file is broken, we do not need to hint
  732       // about a wrong password and can report CRC error only.
  733       if (!Arc.FileHead.Solid)
  734         AnySolidDataUnpackedWell=false; // Reset the flag, because non-solid file is found.
  735       else
  736         if (Arc.FileHead.Method!=0 && Arc.FileHead.UnpSize>0 && ValidCRC)
  737           AnySolidDataUnpackedWell=true;
  738  
  739       bool BrokenFile=false;
  740       
  741       // Checksum is not calculated in skip solid mode for performance reason.
  742       if (!SkipSolid && ShowChecksum)
  743       {
  744         if (ValidCRC)
  745         {
  746           if (Command!='P' && Command!='I' && !Cmd->DisableNames)
  747             mprintf(L"%s%s ",Cmd->DisablePercentage ? L" ":L"\b\b\b\b\b ",
  748               Arc.FileHead.FileHash.Type==HASH_NONE ? L"  ?":St(MOk));
  749         }
  750         else
  751         {
  752           if (Arc.FileHead.Encrypted && (!Arc.FileHead.UsePswCheck || 
  753               Arc.BrokenHeader) && !AnySolidDataUnpackedWell)
  754             uiMsg(UIERROR_CHECKSUMENC,Arc.FileName,ArcFileName);
  755           else
  756             uiMsg(UIERROR_CHECKSUM,Arc.FileName,ArcFileName);
  757           BrokenFile=true;
  758           ErrHandler.SetErrorCode(RARX_CRC);
  759 #ifdef RARDLL
  760           // If we already have ERAR_EOPEN as result of missing volume
  761           // or ERAR_BAD_PASSWORD for RAR5 wrong password,
  762           // we should not replace it with less precise ERAR_BAD_DATA.
  763           if (Cmd->DllError!=ERAR_EOPEN && Cmd->DllError!=ERAR_BAD_PASSWORD)
  764             Cmd->DllError=ERAR_BAD_DATA;
  765 #endif
  766         }
  767       }
  768       else
  769       {
  770         // We check SkipSolid to remove percent for skipped solid files only.
  771         // We must not apply these \b to links with ShowChecksum==false
  772         // and their possible error messages.
  773         if (SkipSolid) 
  774           mprintf(L"\b\b\b\b\b     ");
  775       }
  776 
  777       // If we successfully unpacked a hard link, we wish to set its file
  778       // attributes. Hard link shares file metadata with link target,
  779       // so we do not need to set link time or owner. But when we overwrite
  780       // an existing link, we can call PrepareToDelete(), which affects
  781       // link target attributes as well. So we set link attributes to restore
  782       // both target and link attributes if PrepareToDelete() changed them.
  783       bool SetAttrOnly=LinkEntry && Arc.FileHead.RedirType==FSREDIR_HARDLINK && LinkSuccess;
  784 
  785       if (!TestMode && (Command=='X' || Command=='E') &&
  786           (!LinkEntry || SetAttrOnly || Arc.FileHead.RedirType==FSREDIR_FILECOPY && LinkSuccess) && 
  787           (!BrokenFile || Cmd->KeepBroken))
  788       {
  789         // Below we use DestFileName instead of CurFile.FileName,
  790         // so we can set file attributes also for hard links, which do not
  791         // have the open CurFile. These strings are the same for other items.
  792 
  793         if (!SetAttrOnly)
  794         {
  795           // We could preallocate more space that really written to broken file
  796           // or file with crafted header.
  797           if (Preallocated>0 && (BrokenFile || DataIO.CurUnpWrite!=Preallocated))
  798             CurFile.Truncate();
  799 
  800 
  801           CurFile.SetOpenFileTime(
  802             Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime,
  803             Cmd->xctime==EXTTIME_NONE ? NULL:&Arc.FileHead.ctime,
  804             Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime);
  805           CurFile.Close();
  806 
  807           SetFileHeaderExtra(Cmd,Arc,DestFileName);
  808 
  809           CurFile.SetCloseFileTime(
  810             Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime,
  811             Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime);
  812         }
  813         
  814 #if defined(_WIN_ALL) && !defined(SFX_MODULE)
  815         if (Cmd->SetCompressedAttr &&
  816             (Arc.FileHead.FileAttr & FILE_ATTRIBUTE_COMPRESSED)!=0)
  817           SetFileCompression(DestFileName,true);
  818         if (Cmd->ClearArc)
  819           Arc.FileHead.FileAttr&=~FILE_ATTRIBUTE_ARCHIVE;
  820 #endif
  821         if (!Cmd->IgnoreGeneralAttr && !SetFileAttr(DestFileName,Arc.FileHead.FileAttr))
  822         {
  823           uiMsg(UIERROR_FILEATTR,Arc.FileName,DestFileName);
  824           // Android cannot set file attributes and while UIERROR_FILEATTR
  825           // above is handled by Android RAR silently, this call would cause
  826           // "Operation not permitted" message for every unpacked file.
  827           ErrHandler.SysErrMsg();
  828         }
  829 
  830         PrevProcessed=true;
  831       }
  832     }
  833   }
  834   // It is important to increment it for files, but not dirs. So we extract
  835   // dir with its entire contents, not just dir record only even if dir
  836   // record precedes files.
  837   if (MatchFound)
  838     MatchedArgs++;
  839   if (DataIO.NextVolumeMissing)
  840     return false;
  841   if (!ExtrFile)
  842     if (!Arc.Solid)
  843       Arc.SeekToNext();
  844     else
  845       if (!SkipSolid)
  846         return false;
  847   return true;
  848 }
  849 
  850 
  851 void CmdExtract::UnstoreFile(ComprDataIO &DataIO,int64 DestUnpSize)
  852 {
  853   Array<byte> Buffer(File::CopyBufferSize());
  854   while (true)
  855   {
  856     int ReadSize=DataIO.UnpRead(&Buffer[0],Buffer.Size());
  857     if (ReadSize<=0)
  858       break;
  859     int WriteSize=ReadSize<DestUnpSize ? ReadSize:(int)DestUnpSize;
  860     if (WriteSize>0)
  861     {
  862       DataIO.UnpWrite(&Buffer[0],WriteSize);
  863       DestUnpSize-=WriteSize;
  864     }
  865   }
  866 }
  867 
  868 
  869 bool CmdExtract::ExtractFileCopy(File &New,wchar *ArcName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize)
  870 {
  871   SlashToNative(NameExisting,NameExisting,NameExistingSize); // Not needed for RAR 5.1+ archives.
  872 
  873   File Existing;
  874   if (!Existing.WOpen(NameExisting))
  875   {
  876     uiMsg(UIERROR_FILECOPY,ArcName,NameExisting,NameNew);
  877     uiMsg(UIERROR_FILECOPYHINT,ArcName);
  878 #ifdef RARDLL
  879     Cmd->DllError=ERAR_EREFERENCE;
  880 #endif
  881     return false;
  882   }
  883 
  884   Array<char> Buffer(0x100000);
  885   int64 CopySize=0;
  886 
  887   while (true)
  888   {
  889     Wait();
  890     int ReadSize=Existing.Read(&Buffer[0],Buffer.Size());
  891     if (ReadSize==0)
  892       break;
  893     New.Write(&Buffer[0],ReadSize);
  894     CopySize+=ReadSize;
  895   }
  896 
  897   return true;
  898 }
  899 
  900 
  901 void CmdExtract::ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *DestName,size_t DestSize)
  902 {
  903   wcsncpyz(DestName,Cmd->ExtrPath,DestSize);
  904 
  905   if (*Cmd->ExtrPath!=0)
  906   {
  907      wchar LastChar=*PointToLastChar(Cmd->ExtrPath);
  908     // We need IsPathDiv check here to correctly handle Unix forward slash
  909     // in the end of destination path in Windows: rar x arc dest/
  910     // so we call IsPathDiv first instead of just calling AddEndSlash,
  911     // which checks for only one type of path separator.
  912     // IsDriveDiv is needed for current drive dir: rar x arc d:
  913     if (!IsPathDiv(LastChar) && !IsDriveDiv(LastChar))
  914     {
  915       // Destination path can be without trailing slash if it come from GUI shell.
  916       AddEndSlash(DestName,DestSize);
  917     }
  918   }
  919 
  920 #ifndef SFX_MODULE
  921   if (Cmd->AppendArcNameToPath!=APPENDARCNAME_NONE)
  922   {
  923     switch(Cmd->AppendArcNameToPath)
  924     {
  925       case APPENDARCNAME_DESTPATH: // To subdir of destination path.
  926         wcsncatz(DestName,PointToName(Arc.FirstVolumeName),DestSize);
  927         SetExt(DestName,NULL,DestSize);
  928         break;
  929       case APPENDARCNAME_OWNSUBDIR: // To subdir of archive own dir.
  930         wcsncpyz(DestName,Arc.FirstVolumeName,DestSize);
  931         SetExt(DestName,NULL,DestSize);
  932         break;
  933       case APPENDARCNAME_OWNDIR:  // To archive own dir.
  934         wcsncpyz(DestName,Arc.FirstVolumeName,DestSize);
  935         RemoveNameFromPath(DestName);
  936         break;
  937     }
  938     AddEndSlash(DestName,DestSize);
  939   }
  940 #endif
  941 
  942 #ifndef SFX_MODULE
  943   wchar *ArcPath=*Cmd->ExclArcPath!=0 ? Cmd->ExclArcPath:Cmd->ArcPath;
  944   size_t ArcPathLength=wcslen(ArcPath);
  945   if (ArcPathLength>0)
  946   {
  947     size_t NameLength=wcslen(ArcFileName);
  948     if (NameLength>=ArcPathLength &&  wcsnicompc(ArcPath,ArcFileName,ArcPathLength)==0 &&
  949         (IsPathDiv(ArcPath[ArcPathLength-1]) || 
  950          IsPathDiv(ArcFileName[ArcPathLength]) || ArcFileName[ArcPathLength]==0))
  951     {
  952       ArcFileName+=Min(ArcPathLength,NameLength);
  953       while (IsPathDiv(*ArcFileName))
  954         ArcFileName++;
  955       if (*ArcFileName==0) // Excessive -ap switch.
  956       {
  957         *DestName=0;
  958         return;
  959       }
  960     }
  961   }
  962 #endif
  963 
  964   wchar Command=Cmd->Command[0];
  965   // Use -ep3 only in systems, where disk letters are exist, not in Unix.
  966   bool AbsPaths=Cmd->ExclPath==EXCL_ABSPATH && Command=='X' && IsDriveDiv(':');
  967 
  968   // We do not use any user specified destination paths when extracting
  969   // absolute paths in -ep3 mode.
  970   if (AbsPaths)
  971     *DestName=0;
  972 
  973   if (Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH)
  974     wcsncatz(DestName,PointToName(ArcFileName),DestSize);
  975   else
  976     wcsncatz(DestName,ArcFileName,DestSize);
  977 
  978 #ifdef _WIN_ALL
  979   // Must do after Cmd->ArcPath processing above, so file name and arc path
  980   // trailing spaces are in sync.
  981   if (!Cmd->AllowIncompatNames)
  982     MakeNameCompatible(DestName,DestSize);
  983 #endif
  984 
  985   wchar DiskLetter=toupperw(DestName[0]);
  986 
  987   if (AbsPaths)
  988   {
  989     if (DestName[1]=='_' && IsPathDiv(DestName[2]) &&
  990         DiskLetter>='A' && DiskLetter<='Z')
  991       DestName[1]=':';
  992     else
  993       if (DestName[0]=='_' && DestName[1]=='_')
  994       {
  995         // Convert __server\share to \\server\share.
  996         DestName[0]=CPATHDIVIDER;
  997         DestName[1]=CPATHDIVIDER;
  998       }
  999   }
 1000 }
 1001 
 1002 
 1003 #ifdef RARDLL
 1004 bool CmdExtract::ExtrDllGetPassword()
 1005 {
 1006   if (!Cmd->Password.IsSet())
 1007   {
 1008     if (Cmd->Callback!=NULL)
 1009     {
 1010       wchar PasswordW[MAXPASSWORD];
 1011       *PasswordW=0;
 1012       if (Cmd->Callback(UCM_NEEDPASSWORDW,Cmd->UserData,(LPARAM)PasswordW,ASIZE(PasswordW))==-1)
 1013         *PasswordW=0;
 1014       if (*PasswordW==0)
 1015       {
 1016         char PasswordA[MAXPASSWORD];
 1017         *PasswordA=0;
 1018         if (Cmd->Callback(UCM_NEEDPASSWORD,Cmd->UserData,(LPARAM)PasswordA,ASIZE(PasswordA))==-1)
 1019           *PasswordA=0;
 1020         GetWideName(PasswordA,NULL,PasswordW,ASIZE(PasswordW));
 1021         cleandata(PasswordA,sizeof(PasswordA));
 1022       }
 1023       Cmd->Password.Set(PasswordW);
 1024       cleandata(PasswordW,sizeof(PasswordW));
 1025       Cmd->ManualPassword=true;
 1026     }
 1027     if (!Cmd->Password.IsSet())
 1028       return false;
 1029   }
 1030   return true;
 1031 }
 1032 #endif
 1033 
 1034 
 1035 #ifndef RARDLL
 1036 bool CmdExtract::ExtrGetPassword(Archive &Arc,const wchar *ArcFileName)
 1037 {
 1038   if (!Cmd->Password.IsSet())
 1039   {
 1040     if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password)/* || !Cmd->Password.IsSet()*/)
 1041     {
 1042       // Suppress "test is ok" message if user cancelled the password prompt.
 1043       uiMsg(UIERROR_INCERRCOUNT);
 1044       return false;
 1045     }
 1046     Cmd->ManualPassword=true;
 1047   }
 1048 #if !defined(SILENT)
 1049   else
 1050     if (!GlobalPassword && !Arc.FileHead.Solid)
 1051     {
 1052       eprintf(St(MUseCurPsw),ArcFileName);
 1053       switch(Cmd->AllYes ? 1 : Ask(St(MYesNoAll)))
 1054       {
 1055         case -1:
 1056           ErrHandler.Exit(RARX_USERBREAK);
 1057         case 2:
 1058           if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password))
 1059             return false;
 1060           break;
 1061         case 3:
 1062           GlobalPassword=true;
 1063           break;
 1064       }
 1065     }
 1066 #endif
 1067   return true;
 1068 }
 1069 #endif
 1070 
 1071 
 1072 #if defined(_WIN_ALL) && !defined(SFX_MODULE)
 1073 void CmdExtract::ConvertDosPassword(Archive &Arc,SecPassword &DestPwd)
 1074 {
 1075   if (Arc.Format==RARFMT15 && Arc.FileHead.HostOS==HOST_MSDOS)
 1076   {
 1077     // We need the password in OEM encoding if file was encrypted by
 1078     // native RAR/DOS (not extender based). Let's make the conversion.
 1079     wchar PlainPsw[MAXPASSWORD];
 1080     Cmd->Password.Get(PlainPsw,ASIZE(PlainPsw));
 1081     char PswA[MAXPASSWORD];
 1082     CharToOemBuffW(PlainPsw,PswA,ASIZE(PswA));
 1083     PswA[ASIZE(PswA)-1]=0;
 1084     CharToWide(PswA,PlainPsw,ASIZE(PlainPsw));
 1085     DestPwd.Set(PlainPsw);
 1086     cleandata(PlainPsw,sizeof(PlainPsw));
 1087     cleandata(PswA,sizeof(PswA));
 1088   }
 1089 }
 1090 #endif
 1091 
 1092 
 1093 void CmdExtract::ExtrCreateDir(Archive &Arc,const wchar *ArcFileName)
 1094 {
 1095   if (Cmd->Test)
 1096   {
 1097     if (!Cmd->DisableNames)
 1098     {
 1099       mprintf(St(MExtrTestFile),ArcFileName);
 1100       mprintf(L" %s",St(MOk));
 1101     }
 1102     return;
 1103   }
 1104 
 1105   MKDIR_CODE MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr);
 1106   bool DirExist=false;
 1107   if (MDCode!=MKDIR_SUCCESS)
 1108   {
 1109     DirExist=FileExist(DestFileName);
 1110     if (DirExist && !IsDir(GetFileAttr(DestFileName)))
 1111     {
 1112       // File with name same as this directory exists. Propose user
 1113       // to overwrite it.
 1114       bool UserReject;
 1115       FileCreate(Cmd,NULL,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime);
 1116       DirExist=false;
 1117     }
 1118     if (!DirExist)
 1119     {
 1120       CreatePath(DestFileName,true,Cmd->DisableNames);
 1121       MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr);
 1122       if (MDCode!=MKDIR_SUCCESS && !IsNameUsable(DestFileName))
 1123       {
 1124         uiMsg(UIMSG_CORRECTINGNAME,Arc.FileName);
 1125         wchar OrigName[ASIZE(DestFileName)];
 1126         wcsncpyz(OrigName,DestFileName,ASIZE(OrigName));
 1127         MakeNameUsable(DestFileName,true);
 1128 #ifndef SFX_MODULE
 1129         uiMsg(UIERROR_RENAMING,Arc.FileName,OrigName,DestFileName);
 1130 #endif
 1131         DirExist=FileExist(DestFileName) && IsDir(GetFileAttr(DestFileName));
 1132         if (!DirExist)
 1133         {
 1134           CreatePath(DestFileName,true,Cmd->DisableNames);
 1135           MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr);
 1136         }
 1137       }
 1138     }
 1139   }
 1140   if (MDCode==MKDIR_SUCCESS)
 1141   {
 1142     if (!Cmd->DisableNames)
 1143     {
 1144       mprintf(St(MCreatDir),DestFileName);
 1145       mprintf(L" %s",St(MOk));
 1146     }
 1147     PrevProcessed=true;
 1148   }
 1149   else
 1150     if (DirExist)
 1151     {
 1152       if (!Cmd->IgnoreGeneralAttr)
 1153         SetFileAttr(DestFileName,Arc.FileHead.FileAttr);
 1154       PrevProcessed=true;
 1155     }
 1156     else
 1157     {
 1158       uiMsg(UIERROR_DIRCREATE,Arc.FileName,DestFileName);
 1159       ErrHandler.SysErrMsg();
 1160 #ifdef RARDLL
 1161       Cmd->DllError=ERAR_ECREATE;
 1162 #endif
 1163       ErrHandler.SetErrorCode(RARX_CREATE);
 1164     }
 1165   if (PrevProcessed)
 1166   {
 1167 #if defined(_WIN_ALL) && !defined(SFX_MODULE)
 1168     if (Cmd->SetCompressedAttr &&
 1169         (Arc.FileHead.FileAttr & FILE_ATTRIBUTE_COMPRESSED)!=0 && WinNT()!=WNT_NONE)
 1170       SetFileCompression(DestFileName,true);
 1171 #endif
 1172     SetFileHeaderExtra(Cmd,Arc,DestFileName);
 1173     SetDirTime(DestFileName,
 1174       Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime,
 1175       Cmd->xctime==EXTTIME_NONE ? NULL:&Arc.FileHead.ctime,
 1176       Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime);
 1177   }
 1178 }
 1179 
 1180 
 1181 bool CmdExtract::ExtrCreateFile(Archive &Arc,File &CurFile)
 1182 {
 1183   bool Success=true;
 1184   wchar Command=Cmd->Command[0];
 1185 #if !defined(SFX_MODULE)
 1186   if (Command=='P')
 1187     CurFile.SetHandleType(FILE_HANDLESTD);
 1188 #endif
 1189   if ((Command=='E' || Command=='X') && !Cmd->Test)
 1190   {
 1191     bool UserReject;
 1192     // Specify "write only" mode to avoid OpenIndiana NAS problems
 1193     // with SetFileTime and read+write files.
 1194     if (!FileCreate(Cmd,&CurFile,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime,true))
 1195     {
 1196       Success=false;
 1197       if (!UserReject)
 1198       {
 1199         ErrHandler.CreateErrorMsg(Arc.FileName,DestFileName);
 1200         if (FileExist(DestFileName) && IsDir(GetFileAttr(DestFileName)))
 1201           uiMsg(UIERROR_DIRNAMEEXISTS);
 1202 
 1203 #ifdef RARDLL
 1204         Cmd->DllError=ERAR_ECREATE;
 1205 #endif
 1206         if (!IsNameUsable(DestFileName))
 1207         {
 1208           uiMsg(UIMSG_CORRECTINGNAME,Arc.FileName);
 1209 
 1210           wchar OrigName[ASIZE(DestFileName)];
 1211           wcsncpyz(OrigName,DestFileName,ASIZE(OrigName));
 1212 
 1213           MakeNameUsable(DestFileName,true);
 1214 
 1215           CreatePath(DestFileName,true,Cmd->DisableNames);
 1216           if (FileCreate(Cmd,&CurFile,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime,true))
 1217           {
 1218 #ifndef SFX_MODULE
 1219             uiMsg(UIERROR_RENAMING,Arc.FileName,OrigName,DestFileName);
 1220 #endif
 1221             Success=true;
 1222           }
 1223           else
 1224             ErrHandler.CreateErrorMsg(Arc.FileName,DestFileName);
 1225         }
 1226       }
 1227     }
 1228   }
 1229   return Success;
 1230 }
 1231 
 1232 
 1233 bool CmdExtract::CheckUnpVer(Archive &Arc,const wchar *ArcFileName)
 1234 {
 1235   bool WrongVer;
 1236   if (Arc.Format==RARFMT50) // Both SFX and RAR can unpack RAR 5.0 archives.
 1237     WrongVer=Arc.FileHead.UnpVer>VER_UNPACK5;
 1238   else
 1239   {
 1240 #ifdef SFX_MODULE   // SFX can unpack only RAR 2.9 archives.
 1241     WrongVer=Arc.FileHead.UnpVer!=VER_UNPACK;
 1242 #else               // All formats since 1.3 for RAR.
 1243     WrongVer=Arc.FileHead.UnpVer<13 || Arc.FileHead.UnpVer>VER_UNPACK;
 1244 #endif
 1245   }
 1246 
 1247   // We can unpack stored files regardless of compression version field.
 1248   if (Arc.FileHead.Method==0)
 1249     WrongVer=false;
 1250 
 1251   if (WrongVer)
 1252   {
 1253     ErrHandler.UnknownMethodMsg(Arc.FileName,ArcFileName);
 1254     uiMsg(UIERROR_NEWERRAR,Arc.FileName);
 1255   }
 1256   return !WrongVer;
 1257 }
 1258 
 1259 
 1260 #ifndef SFX_MODULE
 1261 // To speed up solid volumes extraction, try to find a non-first start volume,
 1262 // which still allows to unpack all files. It is possible for independent
 1263 // solid volumes with solid statistics reset in the beginning.
 1264 bool CmdExtract::DetectStartVolume(const wchar *VolName,bool NewNumbering)
 1265 {
 1266   wchar *ArgName=Cmd->FileArgs.GetString();
 1267   Cmd->FileArgs.Rewind();
 1268   if (ArgName!=NULL && (wcscmp(ArgName,L"*")==0 || wcscmp(ArgName,L"*.*")==0))
 1269     return false; // No need to check further for * and *.* masks.
 1270 
 1271   wchar StartName[NM];
 1272   *StartName=0;
 1273   
 1274   // Start search from first volume if all volumes preceding current are available.
 1275   wchar NextName[NM];
 1276   GetFirstVolIfFullSet(VolName,NewNumbering,NextName,ASIZE(NextName));
 1277 
 1278   bool Matched=false;
 1279   while (!Matched)
 1280   {
 1281     Archive Arc(Cmd);
 1282     if (!Arc.Open(NextName) || !Arc.IsArchive(false) || !Arc.Volume)
 1283       break;
 1284 
 1285     bool OpenNext=false;
 1286     while (Arc.ReadHeader()>0)
 1287     {
 1288       Wait();
 1289 
 1290       HEADER_TYPE HeaderType=Arc.GetHeaderType();
 1291       if (HeaderType==HEAD_ENDARC)
 1292       {
 1293         OpenNext|=Arc.EndArcHead.NextVolume; // Allow open next volume.
 1294         break;
 1295       }
 1296       if (HeaderType==HEAD_FILE)
 1297       {
 1298         if (!Arc.FileHead.SplitBefore)
 1299         {
 1300           if (!Arc.FileHead.Solid) // Can start extraction from here.
 1301             wcsncpyz(StartName,NextName,ASIZE(StartName));
 1302 
 1303           if (Cmd->IsProcessFile(Arc.FileHead,NULL,MATCH_WILDSUBPATH,0,NULL,0)!=0)
 1304           {
 1305             Matched=true; // First matched file found, must stop further scan.
 1306             break;
 1307           }
 1308         }
 1309         if (Arc.FileHead.SplitAfter)
 1310         {
 1311           OpenNext=true; // Allow open next volume.
 1312           break;
 1313         }
 1314       }
 1315       Arc.SeekToNext();
 1316     }
 1317     Arc.Close();
 1318 
 1319     if (!OpenNext)
 1320       break;
 1321 
 1322     NextVolumeName(NextName,ASIZE(NextName),!Arc.NewNumbering);
 1323   }
 1324   bool NewStartFound=wcscmp(VolName,StartName)!=0;
 1325   if (NewStartFound) // Found a new volume to start extraction.
 1326     wcsncpyz(ArcName,StartName,ASIZE(ArcName));
 1327   
 1328   return NewStartFound;
 1329 }
 1330 #endif
 1331 
 1332 
 1333 #ifndef SFX_MODULE
 1334 // Return the first volume name if all volumes preceding the specified
 1335 // are available. Otherwise return the specified volume name.
 1336 void CmdExtract::GetFirstVolIfFullSet(const wchar *SrcName,bool NewNumbering,wchar *DestName,size_t DestSize)
 1337 {
 1338   wchar FirstVolName[NM];
 1339   VolNameToFirstName(SrcName,FirstVolName,ASIZE(FirstVolName),NewNumbering);
 1340   wchar NextName[NM];
 1341   wcsncpyz(NextName,FirstVolName,ASIZE(NextName));
 1342   wchar ResultName[NM];
 1343   wcsncpyz(ResultName,SrcName,ASIZE(ResultName));
 1344   while (true)
 1345   {
 1346     if (wcscmp(SrcName,NextName)==0)
 1347     {
 1348       wcsncpyz(ResultName,FirstVolName,DestSize);
 1349       break;
 1350     }
 1351     if (!FileExist(NextName))
 1352       break;
 1353     NextVolumeName(NextName,ASIZE(NextName),!NewNumbering);
 1354   }
 1355   wcsncpyz(DestName,ResultName,DestSize);
 1356 }
 1357 
 1358 #endif