Esempio n. 1
0
// Preprocess those parameters, which must be processed before the rest of
// command line. Return 'false' to stop further processing.
void CommandData::PreprocessArg(const wchar *Arg)
{
  if (IsSwitch(Arg[0]) && !NoMoreSwitches)
  {
    Arg++;
    if (Arg[0]=='-' && Arg[1]==0) // Switch "--".
      NoMoreSwitches=true;
    if (wcsicomp(Arg,L"cfg-")==0)
      ConfigDisabled=true;
    if (wcsnicomp(Arg,L"ilog",4)==0)
    {
      // Ensure that correct log file name is already set
      // if we need to report an error when processing the command line.
      ProcessSwitch(Arg);
      InitLogOptions(LogName,ErrlogCharset);
    }
    if (wcsnicomp(Arg,L"sc",2)==0)
    {
      // Process -sc before reading any file lists.
      ProcessSwitch(Arg);
      if (*LogName!=0)
        InitLogOptions(LogName,ErrlogCharset);
    }
  }
  else
    if (*Command==0)
      wcsncpy(Command,Arg,ASIZE(Command)); // Need for rar.ini.
}
Esempio n. 2
0
void NextVolumeName(wchar *ArcName,uint MaxLength,bool OldNumbering)
{
  wchar *ChPtr;
  if ((ChPtr=GetExt(ArcName))==NULL)
  {
    wcsncatz(ArcName,L".rar",MaxLength);
    ChPtr=GetExt(ArcName);
  }
  else
    if (ChPtr[1]==0 && wcslen(ArcName)<MaxLength-3 || wcsicomp(ChPtr+1,L"exe")==0 || wcsicomp(ChPtr+1,L"sfx")==0)
      wcscpy(ChPtr+1,L"rar");
  if (!OldNumbering)
  {
    ChPtr=GetVolNumPart(ArcName);

    while ((++(*ChPtr))=='9'+1)
    {
      *ChPtr='0';
      ChPtr--;
      if (ChPtr<ArcName || !IsDigit(*ChPtr))
      {
        for (wchar *EndPtr=ArcName+wcslen(ArcName);EndPtr!=ChPtr;EndPtr--)
          *(EndPtr+1)=*EndPtr;
        *(ChPtr+1)='1';
        break;
      }
    }
  }
  else
    if (!IsDigit(*(ChPtr+2)) || !IsDigit(*(ChPtr+3)))
      wcscpy(ChPtr+2,L"00");
    else
    {
      ChPtr+=3;
      while ((++(*ChPtr))=='9'+1)
        if (*(ChPtr-1)=='.')
        {
          *ChPtr='A';
          break;
        }
        else
        {
          *ChPtr='0';
          ChPtr--;
        }
    }
}
Esempio n. 3
0
int wcsicompc(const wchar *Str1,const wchar *Str2)
{
#if defined(_UNIX)
  return(wcscmp(Str1,Str2));
#else
  return(wcsicomp(Str1,Str2));
#endif
}
Esempio n. 4
0
int wcsicompc(const wchar *s1,const wchar *s2) // For path comparison.
{
#if defined(_UNIX)
  return wcscmp(s1,s2);
#else
  return wcsicomp(s1,s2);
#endif
}
Esempio n. 5
0
bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
{
  wchar Command=Cmd->Command[0];
  if (HeaderSize==0)
  {
    if (DataIO.UnpVolume)
    {
#ifdef NOVOLUME
      return false;
#else
      // Supposing we unpack an old RAR volume without end of archive record
      // and last file is not split between volumes.
      if (!MergeArchive(Arc,&DataIO,false,Command))
      {
        ErrHandler.SetErrorCode(RARX_WARNING);
        return false;
      }
#endif
    }
    else
      return false;
  }
  HEADER_TYPE HeaderType=Arc.GetHeaderType();
  if (HeaderType!=HEAD_FILE)
  {
#ifndef SFX_MODULE
    if (HeaderType==HEAD3_OLDSERVICE && PrevExtracted)
      SetExtraInfo20(Cmd,Arc,DestFileName);
#endif
    if (HeaderType==HEAD_SERVICE && PrevExtracted)
      SetExtraInfo(Cmd,Arc,DestFileName);
    if (HeaderType==HEAD_ENDARC)
    {
      if (Arc.EndArcHead.NextVolume)
      {
#ifndef NOVOLUME
        if (!MergeArchive(Arc,&DataIO,false,Command))
        {
          ErrHandler.SetErrorCode(RARX_WARNING);
          return false;
        }
#endif
        Arc.Seek(Arc.CurBlockPos,SEEK_SET);
        return true;
      }
      else
        return false;
    }
    Arc.SeekToNext();
    return true;
  }
  PrevExtracted=false;

  if (!Cmd->Recurse && MatchedArgs>=Cmd->FileArgs.ItemsCount() && AllMatchesExact)
    return false;

  int MatchType=MATCH_WILDSUBPATH;

  bool EqualNames=false;
  wchar MatchedArg[NM];
  int MatchNumber=Cmd->IsProcessFile(Arc.FileHead,&EqualNames,MatchType,MatchedArg,ASIZE(MatchedArg));
  bool MatchFound=MatchNumber!=0;
#ifndef SFX_MODULE
  if (Cmd->ExclPath==EXCL_BASEPATH)
  {
    wcsncpyz(Cmd->ArcPath,MatchedArg,ASIZE(Cmd->ArcPath));
    *PointToName(Cmd->ArcPath)=0;
    if (IsWildcard(Cmd->ArcPath)) // Cannot correctly process path*\* masks here.
      *Cmd->ArcPath=0;
  }
#endif
  if (MatchFound && !EqualNames)
    AllMatchesExact=false;

  Arc.ConvertAttributes();

#if !defined(SFX_MODULE) && !defined(RARDLL)
  if (Arc.FileHead.SplitBefore && FirstFile)
  {
    wchar CurVolName[NM];
    wcsncpyz(CurVolName,ArcName,ASIZE(CurVolName));
    VolNameToFirstName(ArcName,ArcName,ASIZE(ArcName),Arc.NewNumbering);

    if (wcsicomp(ArcName,CurVolName)!=0 && FileExist(ArcName))
    {
      // If first volume name does not match the current name and if such
      // volume name really exists, let's unpack from this first volume.
      Repeat=true;
      return false;
    }
#ifndef RARDLL
    if (!ReconstructDone)
    {
      ReconstructDone=true;
      if (RecVolumesRestore(Cmd,Arc.FileName,true))
      {
        Repeat=true;
        return false;
      }
    }
#endif
    wcsncpyz(ArcName,CurVolName,ASIZE(ArcName));
  }
#endif

  wchar ArcFileName[NM];
  ConvertPath(Arc.FileHead.FileName,ArcFileName);

  if (Arc.FileHead.Version)
  {
    if (Cmd->VersionControl!=1 && !EqualNames)
    {
      if (Cmd->VersionControl==0)
        MatchFound=false;
      int Version=ParseVersionFileName(ArcFileName,false);
      if (Cmd->VersionControl-1==Version)
        ParseVersionFileName(ArcFileName,true);
      else
        MatchFound=false;
    }
  }
  else
    if (!Arc.IsArcDir() && Cmd->VersionControl>1)
      MatchFound=false;

  DataIO.UnpVolume=Arc.FileHead.SplitAfter;
  DataIO.NextVolumeMissing=false;

  Arc.Seek(Arc.NextBlockPos-Arc.FileHead.PackSize,SEEK_SET);

  bool ExtrFile=false;
  bool SkipSolid=false;

#ifndef SFX_MODULE
  if (FirstFile && (MatchFound || Arc.Solid) && Arc.FileHead.SplitBefore)
  {
    if (MatchFound)
    {
      uiMsg(UIERROR_NEEDPREVVOL,Arc.FileName,ArcFileName);
#ifdef RARDLL
      Cmd->DllError=ERAR_BAD_DATA;
#endif
      ErrHandler.SetErrorCode(RARX_OPEN);
    }
    MatchFound=false;
  }

  FirstFile=false;
#endif

  if (MatchFound || (SkipSolid=Arc.Solid)!=0)
  {
    // First common call of uiStartFileExtract. It is done before overwrite
    // prompts, so if SkipSolid state is changed below, we'll need to make
    // additional uiStartFileExtract calls with updated parameters.
    if (!uiStartFileExtract(ArcFileName,!Cmd->Test,Cmd->Test && Command!='I',SkipSolid))
      return false;

    ExtrPrepareName(Arc,ArcFileName,DestFileName,ASIZE(DestFileName));

    // DestFileName can be set empty in case of excessive -ap switch.
    ExtrFile=!SkipSolid && *DestFileName!=0 && !Arc.FileHead.SplitBefore;

    if ((Cmd->FreshFiles || Cmd->UpdateFiles) && (Command=='E' || Command=='X'))
    {
      FindData FD;
      if (FindFile::FastFind(DestFileName,&FD))
      {
        if (FD.mtime >= Arc.FileHead.mtime)
        {
          // If directory already exists and its modification time is newer 
          // than start of extraction, it is likely it was created 
          // when creating a path to one of already extracted items. 
          // In such case we'll better update its time even if archived 
          // directory is older.

          if (!FD.IsDir || FD.mtime<StartTime)
            ExtrFile=false;
        }
      }
      else
        if (Cmd->FreshFiles)
          ExtrFile=false;
    }

    if (Arc.FileHead.Encrypted)
    {
#ifdef RARDLL
      if (!ExtrDllGetPassword())
        return false;
#else
      if (!ExtrGetPassword(Arc,ArcFileName))
      {
        PasswordCancelled=true;
        return false;
      }
#endif
      // Skip only the current encrypted file if empty password is entered.
      if (!Cmd->Password.IsSet())
      {
        ErrHandler.SetErrorCode(RARX_WARNING);
#ifdef RARDLL
        Cmd->DllError=ERAR_MISSING_PASSWORD;
#endif
        ExtrFile=false;
      }
    }

#ifdef RARDLL
    if (*Cmd->DllDestName!=0)
      wcsncpyz(DestFileName,Cmd->DllDestName,ASIZE(DestFileName));
#endif

    if (!CheckUnpVer(Arc,ArcFileName))
    {
      ExtrFile=false;
      ErrHandler.SetErrorCode(RARX_FATAL);
#ifdef RARDLL
      Cmd->DllError=ERAR_UNKNOWN_FORMAT;
#endif
    }

    File CurFile;

    bool LinkEntry=Arc.FileHead.RedirType!=FSREDIR_NONE;
    if (LinkEntry && Arc.FileHead.RedirType!=FSREDIR_FILECOPY)
    {
      if (ExtrFile && Command!='P' && !Cmd->Test)
      {
        // Overwrite prompt for symbolic and hard links.
        bool UserReject=false;
        if (FileExist(DestFileName) && !UserReject)
          FileCreate(Cmd,NULL,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime);
        if (UserReject)
          ExtrFile=false;
      }
    }
    else
      if (Arc.IsArcDir())
      {
        if (!ExtrFile || Command=='P' || Command=='I' || Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH)
          return true;
        TotalFileCount++;
        ExtrCreateDir(Arc,ArcFileName);
        return true;
      }
      else
        if (ExtrFile) // Create files and file copies (FSREDIR_FILECOPY).
          ExtrFile=ExtrCreateFile(Arc,CurFile);

    if (!ExtrFile && Arc.Solid)
    {
      SkipSolid=true;
      ExtrFile=true;

      // We changed SkipSolid, so we need to call uiStartFileExtract
      // with "Skip" parameter to change the operation status 
      // from "extracting" to "skipping". For example, it can be necessary
      // if user answered "No" to overwrite prompt when unpacking
      // a solid archive.
      if (!uiStartFileExtract(ArcFileName,false,false,true))
        return false;
    }
    if (ExtrFile)
    {
      bool TestMode=Cmd->Test || SkipSolid; // Unpack to memory, not to disk.

      if (!SkipSolid)
      {
        if (!TestMode && Command!='P' && CurFile.IsDevice())
        {
          uiMsg(UIERROR_INVALIDNAME,Arc.FileName,DestFileName);
          ErrHandler.WriteError(Arc.FileName,DestFileName);
        }
        TotalFileCount++;
      }
      FileCount++;
#ifndef GUI
      if (Command!='I')
      {
        if (SkipSolid)
          mprintf(St(MExtrSkipFile),ArcFileName);
        else
          switch(Cmd->Test ? 'T':Command) // "Test" can be also enabled by -t switch.
          {
            case 'T':
              mprintf(St(MExtrTestFile),ArcFileName);
              break;
#ifndef SFX_MODULE
            case 'P':
              mprintf(St(MExtrPrinting),ArcFileName);
              break;
#endif
            case 'X':
            case 'E':
              mprintf(St(MExtrFile),DestFileName);
              break;
          }
      }
      if (!Cmd->DisablePercentage)
        mprintf(L"     ");
#endif

      SecPassword FilePassword=Cmd->Password;
#if defined(_WIN_ALL) && !defined(SFX_MODULE)
      ConvertDosPassword(Arc,FilePassword);
#endif

      byte PswCheck[SIZE_PSWCHECK];
      DataIO.SetEncryption(false,Arc.FileHead.CryptMethod,&FilePassword,
             Arc.FileHead.SaltSet ? Arc.FileHead.Salt:NULL,
             Arc.FileHead.InitV,Arc.FileHead.Lg2Count,
             PswCheck,Arc.FileHead.HashKey);
      bool WrongPassword=false;

      // If header is damaged, we cannot rely on password check value,
      // because it can be damaged too.
      if (Arc.FileHead.Encrypted && Arc.FileHead.UsePswCheck &&
          memcmp(Arc.FileHead.PswCheck,PswCheck,SIZE_PSWCHECK)!=0 &&
          !Arc.BrokenHeader)
      {
        uiMsg(UIERROR_BADPSW,Arc.FileName);
        ErrHandler.SetErrorCode(RARX_BADPWD);
        WrongPassword=true;
      }
      DataIO.CurUnpRead=0;
      DataIO.CurUnpWrite=0;
      DataIO.UnpHash.Init(Arc.FileHead.FileHash.Type,Cmd->Threads);
      DataIO.PackedDataHash.Init(Arc.FileHead.FileHash.Type,Cmd->Threads);
      DataIO.SetPackedSizeToRead(Arc.FileHead.PackSize);
      DataIO.SetFiles(&Arc,&CurFile);
      DataIO.SetTestMode(TestMode);
      DataIO.SetSkipUnpCRC(SkipSolid);

#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT)
      if (!TestMode && !WrongPassword && !Arc.BrokenHeader &&
          Arc.FileHead.UnpSize>0xffffffff && (Fat32 || !NotFat32))
      {
        if (!Fat32) // Not detected yet.
          NotFat32=!(Fat32=IsFAT(Cmd->ExtrPath));
        if (Fat32)
          uiMsg(UIMSG_FAT32SIZE); // Inform user about FAT32 size limit.
      }
#endif

      if (!TestMode && !WrongPassword && !Arc.BrokenHeader &&
          (Arc.FileHead.PackSize<<11)>Arc.FileHead.UnpSize &&
          (Arc.FileHead.UnpSize<100000000 || Arc.FileLength()>Arc.FileHead.PackSize))
        CurFile.Prealloc(Arc.FileHead.UnpSize);

      CurFile.SetAllowDelete(!Cmd->KeepBroken);

      bool FileCreateMode=!TestMode && !SkipSolid && Command!='P';
      bool ShowChecksum=true; // Display checksum verification result.

      bool LinkSuccess=true; // Assume success for test mode.
      if (LinkEntry)
      {
        FILE_SYSTEM_REDIRECT Type=Arc.FileHead.RedirType;

        if (Type==FSREDIR_HARDLINK || Type==FSREDIR_FILECOPY)
        {
          wchar NameExisting[NM];
          ExtrPrepareName(Arc,Arc.FileHead.RedirName,NameExisting,ASIZE(NameExisting));
          if (FileCreateMode && *NameExisting!=0) // *NameExisting can be 0 in case of excessive -ap switch.
          {
            if (Type==FSREDIR_HARDLINK)
              LinkSuccess=ExtractHardlink(DestFileName,NameExisting,ASIZE(NameExisting));
            else
              LinkSuccess=ExtractFileCopy(CurFile,Arc.FileName,DestFileName,NameExisting,ASIZE(NameExisting));
          }
        }
        else
          if (Type==FSREDIR_UNIXSYMLINK || Type==FSREDIR_WINSYMLINK || Type==FSREDIR_JUNCTION)
          {
            if (FileCreateMode)
              LinkSuccess=ExtractSymlink(Cmd,DataIO,Arc,DestFileName);
          }
          else
          {
            uiMsg(UIERROR_UNKNOWNEXTRA, Arc.FileName, DestFileName);
            LinkSuccess=false;
          }
          
          if (!LinkSuccess || (Arc.Format==RARFMT15 && !FileCreateMode))
          {
            // RAR 5.x links have a valid data checksum even in case of
            // failure, because they do not store any data.
            // We do not want to display "OK" in this case.
            // For 4.x symlinks we verify the checksum only when extracting,
            // but not when testing an archive.
            ShowChecksum=false;
          }
          PrevExtracted=FileCreateMode && LinkSuccess;
      }
      else
        if (!Arc.FileHead.SplitBefore && !WrongPassword)
        {
          if (Arc.FileHead.Method==0)
            UnstoreFile(DataIO,Arc.FileHead.UnpSize);
          else
          {
#ifdef _ANDROID
            // malloc and new do not report memory allocation errors
            // in Android, so if free memory is set, check it here
            // trying to prevent crash.
            if (Cmd->FreeMem!=0 && Cmd->FreeMem < Arc.FileHead.WinSize)
              throw std::bad_alloc();
#endif
            Unp->Init(Arc.FileHead.WinSize,Arc.FileHead.Solid);
            Unp->SetDestSize(Arc.FileHead.UnpSize);
#ifndef SFX_MODULE
            if (Arc.Format!=RARFMT50 && Arc.FileHead.UnpVer<=15)
              Unp->DoUnpack(15,FileCount>1 && Arc.Solid);
            else
#endif
              Unp->DoUnpack(Arc.FileHead.UnpVer,Arc.FileHead.Solid);
          }
        }

      Arc.SeekToNext();

      // We check for "split after" flag to detect partially extracted files
      // from incomplete volume sets. For them file header contains packed
      // data hash, which must not be compared against unpacked data hash
      // to prevent accidental match. Moreover, for -m0 volumes packed data
      // hash would match truncated unpacked data hash and lead to fake "OK"
      // in incomplete volume set.
      bool ValidCRC=!Arc.FileHead.SplitAfter && DataIO.UnpHash.Cmp(&Arc.FileHead.FileHash,Arc.FileHead.UseHashKey ? Arc.FileHead.HashKey:NULL);

      // We set AnySolidDataUnpackedWell to true if we found at least one
      // valid non-zero solid file in preceding solid stream. If it is true
      // and if current encrypted file is broken, we do not need to hint
      // about a wrong password and can report CRC error only.
      if (!Arc.FileHead.Solid)
        AnySolidDataUnpackedWell=false; // Reset the flag, because non-solid file is found.
      else
        if (Arc.FileHead.Method!=0 && Arc.FileHead.UnpSize>0 && ValidCRC)
          AnySolidDataUnpackedWell=true;
 
      bool BrokenFile=false;
      
      // Checksum is not calculated in skip solid mode for performance reason.
      if (!SkipSolid && ShowChecksum)
      {
        if (!WrongPassword && ValidCRC)
        {
#ifndef GUI
          if (Command!='P' && Command!='I')
            mprintf(L"%s%s ",Cmd->DisablePercentage ? L" ":L"\b\b\b\b\b ",
              Arc.FileHead.FileHash.Type==HASH_NONE ? L"  ?":St(MOk));
#endif
        }
        else
        {
          if (!WrongPassword)
          {
            if (Arc.FileHead.Encrypted && (!Arc.FileHead.UsePswCheck ||
                Arc.BrokenHeader) && !AnySolidDataUnpackedWell)
              uiMsg(UIERROR_CHECKSUMENC,Arc.FileName,ArcFileName);
            else
              uiMsg(UIERROR_CHECKSUM,Arc.FileName,ArcFileName);
          }
          BrokenFile=true;
          ErrHandler.SetErrorCode(RARX_CRC);
#ifdef RARDLL
          // If we already have ERAR_EOPEN as result of missing volume,
          // we should not replace it with less precise ERAR_BAD_DATA.
          if (Cmd->DllError!=ERAR_EOPEN)
            Cmd->DllError=WrongPassword ? ERAR_BAD_PASSWORD : ERAR_BAD_DATA;
#endif
        }
      }
#ifndef GUI
      else
        mprintf(L"\b\b\b\b\b     ");
#endif

      if (!TestMode && !WrongPassword && (Command=='X' || Command=='E') &&
          (!LinkEntry || (Arc.FileHead.RedirType==FSREDIR_FILECOPY && LinkSuccess)) && 
          (!BrokenFile || Cmd->KeepBroken))
      {
        // We could preallocate more space that really written to broken file.
        if (BrokenFile)
          CurFile.Truncate();

#if defined(_WIN_ALL) || defined(_EMX)
        if (Cmd->ClearArc)
          Arc.FileHead.FileAttr&=~FILE_ATTRIBUTE_ARCHIVE;
#endif


        CurFile.SetOpenFileTime(
          Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime,
          Cmd->xctime==EXTTIME_NONE ? NULL:&Arc.FileHead.ctime,
          Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime);
        CurFile.Close();
#if defined(_WIN_ALL) && !defined(SFX_MODULE)
        if (Cmd->SetCompressedAttr &&
            (Arc.FileHead.FileAttr & FILE_ATTRIBUTE_COMPRESSED)!=0)
          SetFileCompression(CurFile.FileName,true);
#endif
#ifdef _UNIX
        if (Cmd->ProcessOwners && Arc.Format==RARFMT50 && Arc.FileHead.UnixOwnerSet)
          SetUnixOwner(Arc,CurFile.FileName);
#endif

        CurFile.SetCloseFileTime(
          Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime,
          Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime);
        if (!Cmd->IgnoreGeneralAttr && !SetFileAttr(CurFile.FileName,Arc.FileHead.FileAttr))
          uiMsg(UIERROR_FILEATTR,Arc.FileName,CurFile.FileName);

        PrevExtracted=true;
      }
    }
  }
  if (MatchFound)
    MatchedArgs++;
  if (DataIO.NextVolumeMissing)
    return false;
  if (!ExtrFile)
  {
    if (!Arc.Solid)
      Arc.SeekToNext();
    else
      if (!SkipSolid)
        return false;
  }
  return true;
}
Esempio n. 6
0
EXTRACT_ARC_CODE CmdExtract::ExtractArchive()
{
  Archive Arc(Cmd);
  if (!Arc.WOpen(ArcName))
    return EXTRACT_ARC_NEXT;

  if (!Arc.IsArchive(true))
  {
#ifndef GUI
    mprintf(St(MNotRAR),ArcName);
#endif
    if (CmpExt(ArcName,L"rar"))
      ErrHandler.SetErrorCode(RARX_WARNING);
    return EXTRACT_ARC_NEXT;
  }

  if (Arc.FailedHeaderDecryption) // Bad archive password.
    return EXTRACT_ARC_NEXT;

#ifndef SFX_MODULE
  if (Arc.Volume && !Arc.FirstVolume)
  {
    wchar FirstVolName[NM];
    VolNameToFirstName(ArcName,FirstVolName,ASIZE(FirstVolName),Arc.NewNumbering);

    // If several volume names from same volume set are specified
    // and current volume is not first in set and first volume is present
    // and specified too, let's skip the current volume.
    if (wcsicomp(ArcName,FirstVolName)!=0 && FileExist(FirstVolName) &&
        Cmd->ArcNames.Search(FirstVolName,false))
      return EXTRACT_ARC_NEXT;
  }
#endif

  int64 VolumeSetSize=0; // Total size of volumes after the current volume.

  if (Arc.Volume)
  {
    // Calculate the total size of all accessible volumes.
    // This size is necessary to display the correct total progress indicator.

    wchar NextName[NM];
    wcscpy(NextName,Arc.FileName);

    while (true)
    {
      // First volume is already added to DataIO.TotalArcSize 
      // in initial TotalArcSize calculation in DoExtract.
      // So we skip it and start from second volume.
      NextVolumeName(NextName,ASIZE(NextName),!Arc.NewNumbering);
      FindData FD;
      if (FindFile::FastFind(NextName,&FD))
        VolumeSetSize+=FD.Size;
      else
        break;
    }
    DataIO.TotalArcSize+=VolumeSetSize;
  }

  ExtractArchiveInit(Arc);

  if (*Cmd->Command=='T' || *Cmd->Command=='I')
    Cmd->Test=true;


  if (*Cmd->Command=='I')
  {
#ifndef GUI   
    Cmd->DisablePercentage=true;
#endif
  }
  else
    uiStartArchiveExtract(!Cmd->Test,ArcName);

  Arc.ViewComment();


  while (1)
  {
    size_t Size=Arc.ReadHeader();


    bool Repeat=false;
    if (!ExtractCurrentFile(Arc,Size,Repeat))
    {
      if (Repeat)
      {
        // If we started extraction from not first volume and need to
        // restart it from first, we must correct DataIO.TotalArcSize
        // for correct total progress display. We subtract the size
        // of current volume and all volumes after it and add the size
        // of new (first) volume.
        FindData OldArc,NewArc;
        if (FindFile::FastFind(Arc.FileName,&OldArc) &&
            FindFile::FastFind(ArcName,&NewArc))
          DataIO.TotalArcSize-=VolumeSetSize+OldArc.Size-NewArc.Size;
        return EXTRACT_ARC_REPEAT;
      }
      else
        break;
    }
  }


  return EXTRACT_ARC_NEXT;
}
Esempio n. 7
0
void CommandData::ProcessSwitch(const wchar *Switch)
{

  switch(toupperw(Switch[0]))
  {
    case '@':
      ListMode=Switch[1]=='+' ? RCLM_ACCEPT_LISTS:RCLM_REJECT_LISTS;
      break;
    case 'A':
      switch(toupperw(Switch[1]))
      {
        case 'C':
          ClearArc=true;
          break;
        case 'D':
          AppendArcNameToPath=true;
          break;
#ifndef SFX_MODULE
        case 'G':
          if (Switch[2]=='-' && Switch[3]==0)
            GenerateArcName=0;
          else
          {
            GenerateArcName=true;
            wcsncpyz(GenerateMask,Switch+2,ASIZE(GenerateMask));
          }
          break;
#endif
        case 'I':
          IgnoreGeneralAttr=true;
          break;
        case 'N': // Reserved for archive name.
          break;
        case 'O':
          AddArcOnly=true;
          break;
        case 'P':
          wcscpy(ArcPath,Switch+2);
          break;
        case 'S':
          SyncFiles=true;
          break;
        default:
          BadSwitch(Switch);
          break;
      }
      break;
    case 'C':
      if (Switch[2]==0)
        switch(toupperw(Switch[1]))
        {
          case '-':
            DisableComment=true;
            break;
          case 'U':
            ConvertNames=NAMES_UPPERCASE;
            break;
          case 'L':
            ConvertNames=NAMES_LOWERCASE;
            break;
        }
      break;
    case 'D':
      if (Switch[2]==0)
        switch(toupperw(Switch[1]))
        {
          case 'S':
            DisableSortSolid=true;
            break;
          case 'H':
            OpenShared=true;
            break;
          case 'F':
            DeleteFiles=true;
            break;
        }
      break;
    case 'E':
      switch(toupperw(Switch[1]))
      {
        case 'P':
          switch(Switch[2])
          {
            case 0:
              ExclPath=EXCL_SKIPWHOLEPATH;
              break;
            case '1':
              ExclPath=EXCL_BASEPATH;
              break;
            case '2':
              ExclPath=EXCL_SAVEFULLPATH;
              break;
            case '3':
              ExclPath=EXCL_ABSPATH;
              break;
          }
          break;
        default:
          if (Switch[1]=='+')
          {
            InclFileAttr|=GetExclAttr(Switch+2);
            InclAttrSet=true;
          }
          else
            ExclFileAttr|=GetExclAttr(Switch+1);
          break;
      }
      break;
    case 'F':
      if (Switch[1]==0)
        FreshFiles=true;
      else
        BadSwitch(Switch);
      break;
    case 'H':
      switch (toupperw(Switch[1]))
      {
        case 'P':
          EncryptHeaders=true;
          if (Switch[2]!=0)
          {
            Password.Set(Switch+2);
            cleandata((void *)Switch,wcslen(Switch)*sizeof(Switch[0]));
          }
          else
            if (!Password.IsSet())
            {
              uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password);
              eprintf(L"\n");
            }
          break;
        default :
          BadSwitch(Switch);
          break;
      }
      break;
    case 'I':
      if (wcsnicomp(Switch+1,L"LOG",3)==0)
      {
        wcsncpyz(LogName,Switch[4]!=0 ? Switch+4:DefLogName,ASIZE(LogName));
        break;
      }
      if (wcsicomp(Switch+1,L"SND")==0)
      {
        Sound=true;
        break;
      }
      if (wcsicomp(Switch+1,L"ERR")==0)
      {
        MsgStream=MSG_STDERR;
        // Set it immediately when parsing the command line, so it also
        // affects messages issued while parsing the command line.
        SetConsoleMsgStream(MSG_STDERR);
        break;
      }
      if (wcsnicomp(Switch+1,L"EML",3)==0)
      {
        wcsncpyz(EmailTo,Switch[4]!=0 ? Switch+4:L"@",ASIZE(EmailTo));
        break;
      }
      if (wcsicomp(Switch+1,L"M")==0)
      {
        MoreInfo=true;
        break;
      }
      if (wcsicomp(Switch+1,L"NUL")==0)
      {
        MsgStream=MSG_NULL;
        SetConsoleMsgStream(MSG_NULL);
        break;
      }
      if (toupperw(Switch[1])=='D')
      {
        for (uint I=2;Switch[I]!=0;I++)
          switch(toupperw(Switch[I]))
          {
            case 'Q':
              MsgStream=MSG_ERRONLY;
              SetConsoleMsgStream(MSG_ERRONLY);
              break;
            case 'C':
              DisableCopyright=true;
              break;
            case 'D':
              DisableDone=true;
              break;
            case 'P':
              DisablePercentage=true;
              break;
          }
        break;
      }
      if (wcsnicomp(Switch+1,L"OFF",3)==0)
      {
        switch(Switch[4])
        {
          case 0:
          case '1':
            Shutdown=POWERMODE_OFF;
            break;
          case '2':
            Shutdown=POWERMODE_HIBERNATE;
            break;
          case '3':
            Shutdown=POWERMODE_SLEEP;
            break;
          case '4':
            Shutdown=POWERMODE_RESTART;
            break;
        }
        break;
      }
      if (wcsicomp(Switch+1,L"VER")==0)
      {
        PrintVersion=true;
        break;
      }
      break;
    case 'K':
      switch(toupperw(Switch[1]))
      {
        case 'B':
          KeepBroken=true;
          break;
        case 0:
          Lock=true;
          break;
      }
      break;
    case 'M':
      switch(toupperw(Switch[1]))
      {
        case 'C':
          {
            const wchar *Str=Switch+2;
            if (*Str=='-')
              for (uint I=0;I<ASIZE(FilterModes);I++)
                FilterModes[I].State=FILTER_DISABLE;
            else
              while (*Str!=0)
              {
                int Param1=0,Param2=0;
                FilterState State=FILTER_AUTO;
                FilterType Type=FILTER_NONE;
                if (IsDigit(*Str))
                {
                  Param1=atoiw(Str);
                  while (IsDigit(*Str))
                    Str++;
                }
                if (*Str==':' && IsDigit(Str[1]))
                {
                  Param2=atoiw(++Str);
                  while (IsDigit(*Str))
                    Str++;
                }
                switch(toupperw(*(Str++)))
                {
                  case 'T': Type=FILTER_PPM;         break;
                  case 'E': Type=FILTER_E8;          break;
                  case 'D': Type=FILTER_DELTA;       break;
                  case 'A': Type=FILTER_AUDIO;       break;
                  case 'C': Type=FILTER_RGB;         break;
                  case 'I': Type=FILTER_ITANIUM;     break;
                  case 'R': Type=FILTER_ARM;         break;
                }
                if (*Str=='+' || *Str=='-')
                  State=*(Str++)=='+' ? FILTER_FORCE:FILTER_DISABLE;
                FilterModes[Type].State=State;
                FilterModes[Type].Param1=Param1;
                FilterModes[Type].Param2=Param2;
              }
            }
          break;
        case 'M':
          break;
        case 'D':
          break;
        case 'S':
          {
            wchar StoreNames[1024];
            wcsncpyz(StoreNames,(Switch[2]==0 ? DefaultStoreList:Switch+2),ASIZE(StoreNames));
            wchar *Names=StoreNames;
            while (*Names!=0)
            {
              wchar *End=wcschr(Names,';');
              if (End!=NULL)
                *End=0;
              if (*Names=='.')
                Names++;
              wchar Mask[NM];
              if (wcspbrk(Names,L"*?.")==NULL)
                swprintf(Mask,ASIZE(Mask),L"*.%ls",Names);
              else
                wcsncpyz(Mask,Names,ASIZE(Mask));
              StoreArgs.AddString(Mask);
              if (End==NULL)
                break;
              Names=End+1;
            }
          }
          break;
#ifdef RAR_SMP
        case 'T':
          Threads=atoiw(Switch+2);
          if (Threads>MaxPoolThreads || Threads<1)
            BadSwitch(Switch);
          else
          {
          }
          break;
#endif
        default:
          Method=Switch[1]-'0';
          if (Method>5 || Method<0)
            BadSwitch(Switch);
          break;
      }
      break;
    case 'N':
    case 'X':
      if (Switch[1]!=0)
      {
        StringList *Args=toupperw(Switch[0])=='N' ? &InclArgs:&ExclArgs;
        if (Switch[1]=='@' && !IsWildcard(Switch))
          ReadTextFile(Switch+2,Args,false,true,FilelistCharset,true,true,true);
        else
          Args->AddString(Switch+1);
      }
      break;
    case 'O':
      switch(toupperw(Switch[1]))
      {
        case '+':
          Overwrite=OVERWRITE_ALL;
          break;
        case '-':
          Overwrite=OVERWRITE_NONE;
          break;
        case 0:
          Overwrite=OVERWRITE_FORCE_ASK;
          break;
#ifdef _WIN_ALL
        case 'C':
          SetCompressedAttr=true;
          break;
#endif
        case 'H':
          SaveHardLinks=true;
          break;


#ifdef SAVE_LINKS
        case 'L':
          SaveSymLinks=true;
          if (toupperw(Switch[2])=='A')
            AbsoluteLinks=true;
          break;
#endif
#ifdef _WIN_ALL
        case 'N':
          if (toupperw(Switch[2])=='I')
            AllowIncompatNames=true;
          break;
#endif
        case 'R':
          Overwrite=OVERWRITE_AUTORENAME;
          break;
#ifdef _WIN_ALL
        case 'S':
          SaveStreams=true;
          break;
#endif
        case 'W':
          ProcessOwners=true;
          break;
        default :
          BadSwitch(Switch);
          break;
      }
      break;
    case 'P':
      if (Switch[1]==0)
      {
        uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password);
        eprintf(L"\n");
      }
      else
      {
        Password.Set(Switch+1);
        cleandata((void *)Switch,wcslen(Switch)*sizeof(Switch[0]));
      }
      break;
#ifndef SFX_MODULE
    case 'Q':
      if (toupperw(Switch[1])=='O')
        switch(toupperw(Switch[2]))
        {
          case 0:
            QOpenMode=QOPEN_AUTO;
            break;
          case '-':
            QOpenMode=QOPEN_NONE;
            break;
          case '+':
            QOpenMode=QOPEN_ALWAYS;
            break;
          default:
            BadSwitch(Switch);
            break;
        }
      else
        BadSwitch(Switch);
      break;
#endif
    case 'R':
      switch(toupperw(Switch[1]))
      {
        case 0:
          Recurse=RECURSE_ALWAYS;
          break;
        case '-':
          Recurse=RECURSE_DISABLE;
          break;
        case '0':
          Recurse=RECURSE_WILDCARDS;
          break;
        case 'I':
          {
            Priority=atoiw(Switch+2);
            if (Priority<0 || Priority>15)
              BadSwitch(Switch);
            const wchar *ChPtr=wcschr(Switch+2,':');
            if (ChPtr!=NULL)
            {
              SleepTime=atoiw(ChPtr+1);
              if (SleepTime>1000)
                BadSwitch(Switch);
              InitSystemOptions(SleepTime);
            }
            SetPriority(Priority);
          }
          break;
      }
      break;
    case 'S':
      if (IsDigit(Switch[1]))
      {
        Solid|=SOLID_COUNT;
        SolidCount=atoiw(&Switch[1]);
      }
      else
        switch(toupperw(Switch[1]))
        {
          case 0:
            Solid|=SOLID_NORMAL;
            break;
          case '-':
            Solid=SOLID_NONE;
            break;
          case 'E':
            Solid|=SOLID_FILEEXT;
            break;
          case 'V':
            Solid|=Switch[2]=='-' ? SOLID_VOLUME_DEPENDENT:SOLID_VOLUME_INDEPENDENT;
            break;
          case 'D':
            Solid|=SOLID_VOLUME_DEPENDENT;
            break;
          case 'L':
            if (IsDigit(Switch[2]))
              FileSizeLess=atoilw(Switch+2);
            break;
          case 'M':
            if (IsDigit(Switch[2]))
              FileSizeMore=atoilw(Switch+2);
            break;
          case 'C':
            {
              bool AlreadyBad=false; // Avoid reporting "bad switch" several times.

              RAR_CHARSET rch=RCH_DEFAULT;
              switch(toupperw(Switch[2]))
              {
                case 'A':
                  rch=RCH_ANSI;
                  break;
                case 'O':
                  rch=RCH_OEM;
                  break;
                case 'U':
                  rch=RCH_UNICODE;
                  break;
                case 'F':
                  rch=RCH_UTF8;
                  break;
                default :
                  BadSwitch(Switch);
                  AlreadyBad=true;
                  break;
              };
              if (!AlreadyBad)
                if (Switch[3]==0)
                  CommentCharset=FilelistCharset=ErrlogCharset=RedirectCharset=rch;
                else
                  for (uint I=3;Switch[I]!=0 && !AlreadyBad;I++)
                    switch(toupperw(Switch[I]))
                    {
                      case 'C':
                        CommentCharset=rch;
                        break;
                      case 'L':
                        FilelistCharset=rch;
                        break;
                      case 'R':
                        RedirectCharset=rch;
                        break;
                      default:
                        BadSwitch(Switch);
                        AlreadyBad=true;
                        break;
                    }
              // Set it immediately when parsing the command line, so it also
              // affects messages issued while parsing the command line.
              SetConsoleRedirectCharset(RedirectCharset);
            }
            break;

        }
      break;
    case 'T':
      switch(toupperw(Switch[1]))
      {
        case 'K':
          ArcTime=ARCTIME_KEEP;
          break;
        case 'L':
          ArcTime=ARCTIME_LATEST;
          break;
        case 'O':
          FileTimeBefore.SetAgeText(Switch+2);
          break;
        case 'N':
          FileTimeAfter.SetAgeText(Switch+2);
          break;
        case 'B':
          FileTimeBefore.SetIsoText(Switch+2);
          break;
        case 'A':
          FileTimeAfter.SetIsoText(Switch+2);
          break;
        case 'S':
          {
            EXTTIME_MODE Mode=EXTTIME_HIGH3;
            bool CommonMode=Switch[2]>='0' && Switch[2]<='4';
            if (CommonMode)
              Mode=(EXTTIME_MODE)(Switch[2]-'0');
            if (Mode==EXTTIME_HIGH1 || Mode==EXTTIME_HIGH2) // '2' and '3' not supported anymore.
              Mode=EXTTIME_HIGH3;
            if (Switch[2]=='-')
              Mode=EXTTIME_NONE;
            if (CommonMode || Switch[2]=='-' || Switch[2]=='+' || Switch[2]==0)
              xmtime=xctime=xatime=Mode;
            else
            {
              if (Switch[3]>='0' && Switch[3]<='4')
                Mode=(EXTTIME_MODE)(Switch[3]-'0');
              if (Mode==EXTTIME_HIGH1 || Mode==EXTTIME_HIGH2) // '2' and '3' not supported anymore.
                Mode=EXTTIME_HIGH3;
              if (Switch[3]=='-')
                Mode=EXTTIME_NONE;
              switch(toupperw(Switch[2]))
              {
                case 'M':
                  xmtime=Mode;
                  break;
                case 'C':
                  xctime=Mode;
                  break;
                case 'A':
                  xatime=Mode;
                  break;
              }
            }
          }
          break;
        case '-':
          Test=false;
          break;
        case 0:
          Test=true;
          break;
        default:
          BadSwitch(Switch);
          break;
      }
      break;
    case 'U':
      if (Switch[1]==0)
        UpdateFiles=true;
      else
        BadSwitch(Switch);
      break;
    case 'V':
      switch(toupperw(Switch[1]))
      {
        case 'P':
          VolumePause=true;
          break;
        case 'E':
          if (toupperw(Switch[2])=='R')
            VersionControl=atoiw(Switch+3)+1;
          break;
        case '-':
          VolSize=0;
          break;
        default:
          VolSize=VOLSIZE_AUTO; // UnRAR -v switch for list command.
          break;
      }
      break;
    case 'W':
      wcsncpyz(TempPath,Switch+1,ASIZE(TempPath));
      AddEndSlash(TempPath,ASIZE(TempPath));
      break;
    case 'Y':
      AllYes=true;
      break;
    case 'Z':
      if (Switch[1]==0)
      {
        // If comment file is not specified, we read data from stdin.
        wcscpy(CommentFile,L"stdin");
      }
      else
        wcsncpyz(CommentFile,Switch+1,ASIZE(CommentFile));
      break;
    case '?' :
      OutHelp(RARX_SUCCESS);
      break;
    default :
      BadSwitch(Switch);
      break;
  }
}
Esempio n. 8
0
// If we find a file, which short name is equal to 'Name', we try to change
// its short name, while preserving the long name. It helps when unpacking
// an archived file, which long name is equal to short name of already
// existing file. Otherwise we would overwrite the already existing file,
// even though its long name does not match the name of unpacking file.
bool UpdateExistingShortName(const wchar *Name)
{
  wchar LongPathName[NM];
  DWORD Res=GetLongPathName(Name,LongPathName,ASIZE(LongPathName));
  if (Res==0 || Res>=ASIZE(LongPathName))
    return false;
  wchar ShortPathName[NM];
  Res=GetShortPathName(Name,ShortPathName,ASIZE(ShortPathName));
  if (Res==0 || Res>=ASIZE(ShortPathName))
    return false;
  wchar *LongName=PointToName(LongPathName);
  wchar *ShortName=PointToName(ShortPathName);

  // We continue only if file has a short name, which does not match its
  // long name, and this short name is equal to name of file which we need
  // to create.
  if (*ShortName==0 || wcsicomp(LongName,ShortName)==0 ||
      wcsicomp(PointToName(Name),ShortName)!=0)
    return false;

  // Generate the temporary new name for existing file.
  wchar NewName[NM];
  *NewName=0;
  for (int I=0;I<10000 && *NewName==0;I+=123)
  {
    // Here we copy the path part of file to create. We'll make the temporary
    // file in the same folder.
    wcsncpyz(NewName,Name,ASIZE(NewName));

    // Here we set the random name part.
    swprintf(PointToName(NewName),ASIZE(NewName),L"rtmp%d",I);
    
    // If such file is already exist, try next random name.
    if (FileExist(NewName))
      *NewName=0;
  }

  // If we could not generate the name not used by any other file, we return.
  if (*NewName==0)
    return false;
  
  // FastFind returns the name without path, but we need the fully qualified
  // name for renaming, so we use the path from file to create and long name
  // from existing file.
  wchar FullName[NM];
  wcsncpyz(FullName,Name,ASIZE(FullName));
  SetName(FullName,LongName,ASIZE(FullName));
  
  // Rename the existing file to randomly generated name. Normally it changes
  // the short name too.
  if (!MoveFile(FullName,NewName))
    return false;

  // Now we need to create the temporary empty file with same name as
  // short name of our already existing file. We do it to occupy its previous
  // short name and not allow to use it again when renaming the file back to
  // its original long name.
  File KeepShortFile;
  bool Created=false;
  if (!FileExist(Name))
    Created=KeepShortFile.Create(Name,FMF_WRITE|FMF_SHAREREAD);

  // Now we rename the existing file from temporary name to original long name.
  // Since its previous short name is occupied by another file, it should
  // get another short name.
  MoveFile(NewName,FullName);

  if (Created)
  {
    // Delete the temporary zero length file occupying the short name,
    KeepShortFile.Close();
    KeepShortFile.Delete();
  }
  // We successfully changed the short name. Maybe sometimes we'll simplify
  // this function by use of SetFileShortName Windows API call.
  // But SetFileShortName is not available in older Windows.
  return true;
}
Esempio n. 9
0
bool CmdExtract::ExtractCurrentFile(CommandData *Cmd,Archive &Arc,size_t HeaderSize,bool &Repeat)
{
  wchar Command=Cmd->Command[0];
  if (HeaderSize==0)
    if (DataIO.UnpVolume)
    {
#ifdef NOVOLUME
      return false;
#else
      if (!MergeArchive(Arc,&DataIO,false,Command))
      {
        ErrHandler.SetErrorCode(RARX_WARNING);
        return false;
      }
#endif
    }
    else
      return false;
  HEADER_TYPE HeaderType=Arc.GetHeaderType();
  if (HeaderType!=HEAD_FILE)
  {
#ifndef SFX_MODULE
    if (HeaderType==HEAD3_OLDSERVICE && PrevExtracted)
      SetExtraInfo20(Cmd,Arc,DestFileName);
#endif
    if (HeaderType==HEAD_SERVICE && PrevExtracted)
      SetExtraInfo(Cmd,Arc,DestFileName);
    if (HeaderType==HEAD_ENDARC)
      if (Arc.EndArcHead.NextVolume)
      {
#ifndef NOVOLUME
        if (!MergeArchive(Arc,&DataIO,false,Command))
        {
          ErrHandler.SetErrorCode(RARX_WARNING);
          return false;
        }
#endif
        Arc.Seek(Arc.CurBlockPos,SEEK_SET);
        return true;
      }
      else
        return false;
    Arc.SeekToNext();
    return true;
  }
  PrevExtracted=false;

  if (!Cmd->Recurse && MatchedArgs>=Cmd->FileArgs.ItemsCount() && AllMatchesExact)
    return false;

  int MatchType=MATCH_WILDSUBPATH;

  bool EqualNames=false;
  int MatchNumber=Cmd->IsProcessFile(Arc.FileHead,&EqualNames,MatchType);
  bool ExactMatch=MatchNumber!=0;
#ifndef SFX_MODULE
  if (Cmd->ExclPath==EXCL_BASEPATH)
  {
    *Cmd->ArcPath=0;
    if (ExactMatch)
    {
      Cmd->FileArgs.Rewind();
      if (Cmd->FileArgs.GetString(Cmd->ArcPath,ASIZE(Cmd->ArcPath),MatchNumber-1))
        *PointToName(Cmd->ArcPath)=0;
    }
  }
#endif
  if (ExactMatch && !EqualNames)
    AllMatchesExact=false;

  Arc.ConvertAttributes();

#if !defined(SFX_MODULE) && !defined(RARDLL)
  if (Arc.FileHead.SplitBefore && FirstFile)
  {
    wchar CurVolName[NM];
    wcsncpyz(CurVolName,ArcName,ASIZE(CurVolName));
    VolNameToFirstName(ArcName,ArcName,Arc.NewNumbering);

    if (wcsicomp(ArcName,CurVolName)!=0 && FileExist(ArcName))
    {
      // If first volume name does not match the current name and if such
      // volume name really exists, let's unpack from this first volume.
      Repeat=true;
      return false;
    }
#ifndef RARDLL
    if (!ReconstructDone)
    {
      ReconstructDone=true;

      if (RecVolumesRestore(Cmd,Arc.FileName,true))
      {
        Repeat=true;
        return false;
      }
    }
#endif
    wcsncpyz(ArcName,CurVolName,ASIZE(ArcName));
  }
#endif

  wchar ArcFileName[NM];
  ConvertPath(Arc.FileHead.FileName,ArcFileName);

  if (Arc.FileHead.Version)
  {
    if (Cmd->VersionControl!=1 && !EqualNames)
    {
      if (Cmd->VersionControl==0)
        ExactMatch=false;
      int Version=ParseVersionFileName(ArcFileName,false);
      if (Cmd->VersionControl-1==Version)
        ParseVersionFileName(ArcFileName,true);
      else
        ExactMatch=false;
    }
  }
  else
    if (!Arc.IsArcDir() && Cmd->VersionControl>1)
      ExactMatch=false;

  DataIO.UnpVolume=Arc.FileHead.SplitAfter;
  DataIO.NextVolumeMissing=false;

  Arc.Seek(Arc.NextBlockPos-Arc.FileHead.PackSize,SEEK_SET);

  bool ExtrFile=false;
  bool SkipSolid=false;

#ifndef SFX_MODULE
  if (FirstFile && (ExactMatch || Arc.Solid) && Arc.FileHead.SplitBefore)
  {
    if (ExactMatch)
    {
      Log(Arc.FileName,St(MUnpCannotMerge),ArcFileName);
#ifdef RARDLL
      Cmd->DllError=ERAR_BAD_DATA;
#endif
      ErrHandler.SetErrorCode(RARX_OPEN);
    }
    ExactMatch=false;
  }

  FirstFile=false;
#endif

  if (ExactMatch || (SkipSolid=Arc.Solid)!=0)
  {

    ExtrPrepareName(Cmd,Arc,ArcFileName,DestFileName,ASIZE(DestFileName));

    // DestFileName can be set empty in case of excessive -ap switch.
    ExtrFile=!SkipSolid && *DestFileName!=0 && !Arc.FileHead.SplitBefore;

    if ((Cmd->FreshFiles || Cmd->UpdateFiles) && (Command=='E' || Command=='X'))
    {
      FindData FD;
      if (FindFile::FastFind(DestFileName,&FD))
      {
        if (FD.mtime >= Arc.FileHead.mtime)
        {
          // If directory already exists and its modification time is newer 
          // than start of extraction, it is likely it was created 
          // when creating a path to one of already extracted items. 
          // In such case we'll better update its time even if archived 
          // directory is older.

          if (!FD.IsDir || FD.mtime<StartTime)
            ExtrFile=false;
        }
      }
      else
        if (Cmd->FreshFiles)
          ExtrFile=false;
    }

    if (Arc.FileHead.Encrypted)
    {
#ifdef RARDLL
      if (!ExtrDllGetPassword(Cmd))
        return false;
#else
      if (!ExtrGetPassword(Cmd,Arc,ArcFileName))
      {
        PasswordCancelled=true;
        return false;
      }
#endif
      // Skip only the current encrypted file if empty password is entered.
      if (!Password.IsSet())
      {
        ErrHandler.SetErrorCode(RARX_WARNING);
#ifdef RARDLL
        Cmd->DllError=ERAR_MISSING_PASSWORD;
#endif
        ExtrFile=false;
      }
    }

#ifdef RARDLL
    if (*Cmd->DllDestName!=0)
    {
      wcsncpyz(DestFileName,Cmd->DllDestName,ASIZE(DestFileName));

//      Do we need this code?
//      if (Cmd->DllOpMode!=RAR_EXTRACT)
//        ExtrFile=false;
    }
#endif

    if (!CheckUnpVer(Arc,ArcFileName))
    {
      ExtrFile=false;
      ErrHandler.SetErrorCode(RARX_WARNING);
#ifdef RARDLL
      Cmd->DllError=ERAR_UNKNOWN_FORMAT;
#endif
    }

    File CurFile;

    bool LinkEntry=Arc.FileHead.RedirType!=FSREDIR_NONE;
    if (LinkEntry && Arc.FileHead.RedirType!=FSREDIR_FILECOPY)
    {
      if (ExtrFile && Command!='P' && !Cmd->Test)
      {
        // Overwrite prompt for symbolic and hard links.
        bool UserReject=false;
        if (FileExist(DestFileName) && !UserReject)
          FileCreate(Cmd,NULL,DestFileName,ASIZE(DestFileName),Cmd->Overwrite,&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime);
        if (UserReject)
          ExtrFile=false;
      }
    }
    else
      if (Arc.IsArcDir())
      {
        if (!ExtrFile || Command=='P' || Command=='I' || Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH)
          return true;
        TotalFileCount++;
        ExtrCreateDir(Cmd,Arc,ArcFileName);
        return true;
      }
      else
        if (ExtrFile) // Create files and file copies (FSREDIR_FILECOPY).
          ExtrFile=ExtrCreateFile(Cmd,Arc,CurFile);

    if (!ExtrFile && Arc.Solid)
    {
      SkipSolid=true;
      ExtrFile=true;

    }
    if (ExtrFile)
    {
      bool TestMode=Cmd->Test || SkipSolid; // Unpack to memory, not to disk.

      if (!SkipSolid)
      {
        if (!TestMode && Command!='P' && CurFile.IsDevice())
        {
          Log(Arc.FileName,St(MInvalidName),DestFileName);
          ErrHandler.WriteError(Arc.FileName,DestFileName);
        }
        TotalFileCount++;
      }
      FileCount++;
#ifndef GUI
      if (Command!='I')
        if (SkipSolid)
          mprintf(St(MExtrSkipFile),ArcFileName);
        else
          switch(Cmd->Test ? 'T':Command) // "Test" can be also enabled by -t switch.
          {
            case 'T':
              mprintf(St(MExtrTestFile),ArcFileName);
              break;
#ifndef SFX_MODULE
            case 'P':
              mprintf(St(MExtrPrinting),ArcFileName);
              break;
#endif
            case 'X':
            case 'E':
              mprintf(St(MExtrFile),DestFileName);
              break;
          }
      if (!Cmd->DisablePercentage)
        mprintf(L"     ");
#endif

      SecPassword FilePassword=Password;
#if defined(_WIN_ALL) && !defined(SFX_MODULE)
      ConvertDosPassword(Arc,FilePassword);
#endif

      byte PswCheck[SIZE_PSWCHECK];
      DataIO.SetEncryption(false,Arc.FileHead.CryptMethod,&FilePassword,
             Arc.FileHead.SaltSet ? Arc.FileHead.Salt:NULL,
             Arc.FileHead.InitV,Arc.FileHead.Lg2Count,
             PswCheck,Arc.FileHead.HashKey);
      bool WrongPassword=false;

      // If header is damaged, we cannot rely on password check value,
      // because it can be damaged too.
      if (Arc.FileHead.Encrypted && Arc.FileHead.UsePswCheck &&
          memcmp(Arc.FileHead.PswCheck,PswCheck,SIZE_PSWCHECK)!=0 &&
          !Arc.BrokenHeader)
      {
        Log(Arc.FileName,St(MWrongPassword));
        ErrHandler.SetErrorCode(RARX_BADPWD);
        WrongPassword=true;
      }
      DataIO.CurUnpRead=0;
      DataIO.CurUnpWrite=0;
      DataIO.UnpHash.Init(Arc.FileHead.FileHash.Type,Cmd->Threads);
      DataIO.PackedDataHash.Init(Arc.FileHead.FileHash.Type,Cmd->Threads);
      DataIO.SetPackedSizeToRead(Arc.FileHead.PackSize);
      DataIO.SetFiles(&Arc,&CurFile);
      DataIO.SetTestMode(TestMode);
      DataIO.SetSkipUnpCRC(SkipSolid);
      if (!TestMode && !WrongPassword && !Arc.BrokenHeader &&
          (Arc.FileHead.PackSize<<11)>Arc.FileHead.UnpSize &&
          (Arc.FileHead.UnpSize<100000000 || Arc.FileLength()>Arc.FileHead.PackSize))
        CurFile.Prealloc(Arc.FileHead.UnpSize);

      CurFile.SetAllowDelete(!Cmd->KeepBroken);

      bool FileCreateMode=!TestMode && !SkipSolid && Command!='P';
      bool ShowChecksum=true; // Display checksum verification result.

      bool LinkSuccess=true; // Assume success for test mode.
      if (LinkEntry)
      {
        FILE_SYSTEM_REDIRECT Type=Arc.FileHead.RedirType;

        if (Type==FSREDIR_HARDLINK || Type==FSREDIR_FILECOPY)
        {
          wchar NameExisting[NM];
          ExtrPrepareName(Cmd,Arc,Arc.FileHead.RedirName,NameExisting,ASIZE(NameExisting));
          if (FileCreateMode && *NameExisting!=0) // *NameExisting can be 0 in case of excessive -ap switch.
            if (Type==FSREDIR_HARDLINK)
              LinkSuccess=ExtractHardlink(DestFileName,NameExisting,ASIZE(NameExisting));
            else
              LinkSuccess=ExtractFileCopy(Cmd,CurFile,Arc.FileName,DestFileName,NameExisting,ASIZE(NameExisting));
        }
        else
          if (Type==FSREDIR_UNIXSYMLINK || Type==FSREDIR_WINSYMLINK || Type==FSREDIR_JUNCTION)
          {
            if (FileCreateMode)
              LinkSuccess=ExtractSymlink(Cmd,DataIO,Arc,DestFileName);
          }
          else
          {
#ifndef SFX_MODULE
            Log(Arc.FileName,St(MUnknownExtra),DestFileName);
#endif
            LinkSuccess=false;
          }
          
          if (!LinkSuccess || Arc.Format==RARFMT15 && !FileCreateMode)
          {
            // RAR 5.x links have a valid data checksum even in case of
            // failure, because they do not store any data.
            // We do not want to display "OK" in this case.
            // For 4.x symlinks we verify the checksum only when extracting,
            // but not when testing an archive.
            ShowChecksum=false;
          }
          PrevExtracted=FileCreateMode && LinkSuccess;
      }
      else
        if (!Arc.FileHead.SplitBefore && !WrongPassword)
          if (Arc.FileHead.Method==0)
            UnstoreFile(DataIO,Arc.FileHead.UnpSize);
          else
          {
            Unp->Init(Arc.FileHead.WinSize,Arc.FileHead.Solid);
            Unp->SetDestSize(Arc.FileHead.UnpSize);
#ifndef SFX_MODULE
            if (Arc.Format!=RARFMT50 && Arc.FileHead.UnpVer<=15)
              Unp->DoUnpack(15,FileCount>1 && Arc.Solid);
            else
#endif
              Unp->DoUnpack(Arc.FileHead.UnpVer,Arc.FileHead.Solid);
          }

      Arc.SeekToNext();

      bool ValidCRC=DataIO.UnpHash.Cmp(&Arc.FileHead.FileHash,Arc.FileHead.UseHashKey ? Arc.FileHead.HashKey:NULL);

      // We set AnySolidDataUnpackedWell to true if we found at least one
      // valid non-zero solid file in preceding solid stream. If it is true
      // and if current encrypted file is broken, we do not need to hint
      // about a wrong password and can report CRC error only.
      if (!Arc.FileHead.Solid)
        AnySolidDataUnpackedWell=false; // Reset the flag, because non-solid file is found.
      else
        if (Arc.FileHead.Method!=0 && Arc.FileHead.UnpSize>0 && ValidCRC)
          AnySolidDataUnpackedWell=true;
 
      bool BrokenFile=false;
      
      // Checksum is not calculated in skip solid mode for performance reason.
      if (!SkipSolid && ShowChecksum)
      {
        if (!WrongPassword && ValidCRC)
        {
#ifndef GUI
          if (Command!='P' && Command!='I')
            mprintf(L"%s%s ",Cmd->DisablePercentage ? L" ":L"\b\b\b\b\b ",
              Arc.FileHead.FileHash.Type==HASH_NONE ? L"  ?":St(MOk));
#endif
        }
        else
        {
          if (!WrongPassword)
            if (Arc.FileHead.Encrypted && (!Arc.FileHead.UsePswCheck || 
                Arc.BrokenHeader) && !AnySolidDataUnpackedWell)
            {
              Log(Arc.FileName,St(MEncrBadCRC),ArcFileName);
            }
            else
            {
              Log(Arc.FileName,St(MCRCFailed),ArcFileName);
            }
          BrokenFile=true;
          ErrHandler.SetErrorCode(RARX_CRC);
#ifdef RARDLL
          // If we already have ERAR_EOPEN as result of missing volume,
          // we should not replace it with less precise ERAR_BAD_DATA.
          if (Cmd->DllError!=ERAR_EOPEN)
            Cmd->DllError=ERAR_BAD_DATA;
#endif
        }
      }
#ifndef GUI
      else
        mprintf(L"\b\b\b\b\b     ");
#endif

      if (!TestMode && !WrongPassword && (Command=='X' || Command=='E') &&
          (!LinkEntry || Arc.FileHead.RedirType==FSREDIR_FILECOPY && LinkSuccess) && 
          (!BrokenFile || Cmd->KeepBroken))
      {
        // We could preallocate more space that really written to broken file.
        if (BrokenFile)
          CurFile.Truncate();

#if defined(_WIN_ALL) || defined(_EMX)
        if (Cmd->ClearArc)
          Arc.FileHead.FileAttr&=~FILE_ATTRIBUTE_ARCHIVE;
#endif


        CurFile.SetOpenFileTime(
          Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime,
          Cmd->xctime==EXTTIME_NONE ? NULL:&Arc.FileHead.ctime,
          Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime);
        CurFile.Close();
#if defined(_WIN_ALL) && !defined(SFX_MODULE)
        if (Cmd->SetCompressedAttr &&
            (Arc.FileHead.FileAttr & FILE_ATTRIBUTE_COMPRESSED)!=0)
          SetFileCompression(CurFile.FileName,true);
#endif
#ifdef _UNIX
        if (Cmd->ProcessOwners && Arc.Format==RARFMT50 && Arc.FileHead.UnixOwnerSet)
          SetUnixOwner(Arc,CurFile.FileName);
#endif

        CurFile.SetCloseFileTime(
          Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime,
          Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime);
        if (!Cmd->IgnoreGeneralAttr)
          SetFileAttr(CurFile.FileName,Arc.FileHead.FileAttr);
        PrevExtracted=true;
      }
    }
  }
  if (ExactMatch)
    MatchedArgs++;
  if (DataIO.NextVolumeMissing)
    return false;
  if (!ExtrFile)
    if (!Arc.Solid)
      Arc.SeekToNext();
    else
      if (!SkipSolid)
        return false;
  return true;
}
Esempio n. 10
0
// 'Ext' is an extension without the leading dot, like L"rar".
bool CmpExt(const wchar *Name,const wchar *Ext)
{
  wchar *NameExt=GetExt(Name);
  return NameExt!=NULL && wcsicomp(NameExt+1,Ext)==0;
}
Esempio n. 11
0
// If we find a file, which short name is equal to 'Name', we try to change
// its short name, while preserving the long name. It helps when unpacking
// an archived file, which long name is equal to short name of already
// existing file. Otherwise we would overwrite the already existing file,
// even though its long name does not match the name of unpacking file.
bool UpdateExistingShortName(wchar *Name)
{
  // 'Name' is the name of file which we want to create. Let's check
  // if file with such name is exist. If it does not, we return.
  FindData fd;
  if (!FindFile::FastFind(NULL,Name,&fd))
    return(false);

  // We continue only if file has a short name, which does not match its
  // long name, and this short name is equal to name of file which we need
  // to create.
  if (*fd.ShortName==0 || wcsicomp(PointToName(fd.NameW),fd.ShortName)==0 ||
      wcsicomp(PointToName(Name),fd.ShortName)!=0)
    return(false);

  // Generate the temporary new name for existing file.
  wchar NewName[NM];
  *NewName=0;
  for (int I=0;I<10000 && *NewName==0;I+=123)
  {
    // Here we copy the path part of file to create. We'll make the temporary
    // file in the same folder.
    wcsncpyz(NewName,Name,ASIZE(NewName));

    // Here we set the random name part.
    sprintfw(PointToName(NewName),ASIZE(NewName),L"rtmp%d",I);
    
    // If such file is already exist, try next random name.
    if (FileExist(NULL,NewName))
      *NewName=0;
  }

  // If we could not generate the name not used by any other file, we return.
  if (*NewName==0)
    return(false);
  
  // FastFind returns the name without path, but we need the fully qualified
  // name for renaming, so we use the path from file to create and long name
  // from existing file.
  wchar FullName[NM];
  wcsncpyz(FullName,Name,ASIZE(FullName));
  wcscpy(PointToName(FullName),PointToName(fd.NameW));
  
  // Rename the existing file to randomly generated name. Normally it changes
  // the short name too.
  if (!MoveFileW(FullName,NewName))
    return(false);

  // Now we need to create the temporary empty file with same name as
  // short name of our already existing file. We do it to occupy its previous
  // short name and not allow to use it again when renaming the file back to
  // its original long name.
  File KeepShortFile;
  bool Created=false;
  if (!FileExist(NULL,Name))
    Created=KeepShortFile.Create(NULL,Name);

  // Now we rename the existing file from temporary name to original long name.
  // Since its previous short name is occupied by another file, it should
  // get another short name.
  MoveFileW(NewName,FullName);

  if (Created)
  {
    // Delete the temporary zero length file occupying the short name,
    KeepShortFile.Close();
    KeepShortFile.Delete();
  }
  // We successfully changed the short name. Maybe sometimes we'll simplify
  // this function by use of SetFileShortName Windows API call.
  // But SetFileShortName is not available in older Windows.
  return(true);
}
Esempio n. 12
0
bool RecVolumes3::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
{
  wchar ArcName[NM];
  wcscpy(ArcName,Name);
  wchar *Ext=GetExt(ArcName);
  bool NewStyle=false;
  bool RevName=Ext!=NULL && wcsicomp(Ext,L".rev")==0;
  if (RevName)
  {
    for (int DigitGroup=0;Ext>ArcName && DigitGroup<3;Ext--)
      if (!IsDigit(*Ext))
        if (IsDigit(*(Ext-1)) && (*Ext=='_' || DigitGroup<2))
          DigitGroup++;
        else
          if (DigitGroup<2)
          {
            NewStyle=true;
            break;
          }
    while (IsDigit(*Ext) && Ext>ArcName+1)
      Ext--;
    wcscpy(Ext,L"*.*");
    
    FindFile Find;
    Find.SetMask(ArcName);
    FindData fd;
    while (Find.Next(&fd))
    {
      Archive Arc(Cmd);
      if (Arc.WOpen(fd.Name) && Arc.IsArchive(true))
      {
        wcscpy(ArcName,fd.Name);
        break;
      }
    }
  }

  Archive Arc(Cmd);
  if (!Arc.WCheckOpen(ArcName))
    return false;
  if (!Arc.Volume)
  {
#ifndef SILENT
    Log(ArcName,St(MNotVolume),ArcName);
#endif
    return false;
  }
  bool NewNumbering=Arc.NewNumbering;
  Arc.Close();

  wchar *VolNumStart=VolNameToFirstName(ArcName,ArcName,NewNumbering);
  wchar RecVolMask[NM];
  wcscpy(RecVolMask,ArcName);
  size_t BaseNamePartLength=VolNumStart-ArcName;
  wcscpy(RecVolMask+BaseNamePartLength,L"*.rev");

#ifndef SILENT
  int64 RecFileSize=0;
#endif

  // We cannot display "Calculating CRC..." message here, because we do not
  // know if we'll find any recovery volumes. We'll display it after finding
  // the first recovery volume.
  bool CalcCRCMessageDone=false;

  FindFile Find;
  Find.SetMask(RecVolMask);
  FindData RecData;
  int FileNumber=0,RecVolNumber=0,FoundRecVolumes=0,MissingVolumes=0;
  wchar PrevName[NM];
  while (Find.Next(&RecData))
  {
    wchar *CurName=RecData.Name;
    int P[3];
    if (!RevName && !NewStyle)
    {
      NewStyle=true;

      wchar *Dot=GetExt(CurName);
      if (Dot!=NULL)
      {
        int LineCount=0;
        Dot--;
        while (Dot>CurName && *Dot!='.')
        {
          if (*Dot=='_')
            LineCount++;
          Dot--;
        }
        if (LineCount==2)
          NewStyle=false;
      }
    }
    if (NewStyle)
    {
      if (!CalcCRCMessageDone)
      {
#ifndef SILENT
        mprintf(St(MCalcCRCAllVol));
#endif
        CalcCRCMessageDone=true;
      }
      
#ifndef SILENT
        mprintf(L"\n%s",CurName);
#endif

      File CurFile;
      CurFile.TOpen(CurName);
      CurFile.Seek(0,SEEK_END);
      int64 Length=CurFile.Tell();
      CurFile.Seek(Length-7,SEEK_SET);
      for (int I=0;I<3;I++)
        P[2-I]=CurFile.GetByte()+1;
      uint FileCRC=0;
      for (int I=0;I<4;I++)
        FileCRC|=CurFile.GetByte()<<(I*8);
      uint CalcCRC;
      CalcFileSum(&CurFile,&CalcCRC,NULL,Cmd->Threads,Length-4);
      if (FileCRC!=CalcCRC)
      {
#ifndef SILENT
        mprintf(St(MCRCFailed),CurName);
#endif
        continue;
      }
    }
    else
    {
      wchar *Dot=GetExt(CurName);
      if (Dot==NULL)
        continue;
      bool WrongParam=false;
      for (size_t I=0;I<ASIZE(P);I++)
      {
        do
        {
          Dot--;
        } while (IsDigit(*Dot) && Dot>=CurName+BaseNamePartLength);
        P[I]=atoiw(Dot+1);
        if (P[I]==0 || P[I]>255)
          WrongParam=true;
      }
      if (WrongParam)
        continue;
    }
    if (P[1]+P[2]>255)
      continue;
    if (RecVolNumber!=0 && RecVolNumber!=P[1] || FileNumber!=0 && FileNumber!=P[2])
    {
#ifndef SILENT
      Log(NULL,St(MRecVolDiffSets),CurName,PrevName);
#endif
      return(false);
    }
    RecVolNumber=P[1];
    FileNumber=P[2];
    wcscpy(PrevName,CurName);
    File *NewFile=new File;
    NewFile->TOpen(CurName);
    SrcFile[FileNumber+P[0]-1]=NewFile;
    FoundRecVolumes++;
#ifndef SILENT
    if (RecFileSize==0)
      RecFileSize=NewFile->FileLength();
#endif
  }
#ifndef SILENT
  if (!Silent || FoundRecVolumes!=0)
  {
    mprintf(St(MRecVolFound),FoundRecVolumes);
  }
#endif
  if (FoundRecVolumes==0)
    return(false);

  bool WriteFlags[256];
  memset(WriteFlags,0,sizeof(WriteFlags));

  wchar LastVolName[NM];
  *LastVolName=0;

  for (int CurArcNum=0;CurArcNum<FileNumber;CurArcNum++)
  {
    Archive *NewFile=new Archive(Cmd);
    bool ValidVolume=FileExist(ArcName);
    if (ValidVolume)
    {
      NewFile->TOpen(ArcName);
      ValidVolume=NewFile->IsArchive(false);
      if (ValidVolume)
      {
        while (NewFile->ReadHeader()!=0)
        {
          if (NewFile->GetHeaderType()==HEAD_ENDARC)
          {
#ifndef SILENT
            mprintf(L"\n%s",ArcName);
#endif
            if (NewFile->EndArcHead.DataCRC)
            {
              uint CalcCRC;
              CalcFileSum(NewFile,&CalcCRC,NULL,Cmd->Threads,NewFile->CurBlockPos);
              if (NewFile->EndArcHead.ArcDataCRC!=CalcCRC)
              {
                ValidVolume=false;
#ifndef SILENT
                mprintf(St(MCRCFailed),ArcName);
#endif
              }
            }
            break;
          }
          NewFile->SeekToNext();
        }
      }
      if (!ValidVolume)
      {
        NewFile->Close();
        wchar NewName[NM];
        wcscpy(NewName,ArcName);
        wcscat(NewName,L".bad");
#ifndef SILENT
        mprintf(St(MBadArc),ArcName);
        mprintf(St(MRenaming),ArcName,NewName);
#endif
        RenameFile(ArcName,NewName);
      }
      NewFile->Seek(0,SEEK_SET);
    }
    if (!ValidVolume)
    {
      // It is important to return 'false' instead of aborting here,
      // so if we are called from extraction, we will be able to continue
      // extracting. It may happen if .rar and .rev are on read-only disks
      // like CDs.
      if (!NewFile->Create(ArcName))
      {
        // We need to display the title of operation before the error message,
        // to make clear for user that create error is related to recovery 
        // volumes. This is why we cannot use WCreate call here. Title must be
        // before create error, not after that.
#ifndef SILENT
        mprintf(St(MReconstructing));
#endif
        ErrHandler.CreateErrorMsg(NULL,ArcName);
        return false;
      }

      WriteFlags[CurArcNum]=true;
      MissingVolumes++;

      if (CurArcNum==FileNumber-1)
        wcscpy(LastVolName,ArcName);

#ifndef SILENT
      mprintf(St(MAbsNextVol),ArcName);
#endif
    }
    SrcFile[CurArcNum]=(File*)NewFile;
    NextVolumeName(ArcName,ASIZE(ArcName),!NewNumbering);
  }

#ifndef SILENT
  mprintf(St(MRecVolMissing),MissingVolumes);
#endif

  if (MissingVolumes==0)
  {
#ifndef SILENT
    mprintf(St(MRecVolAllExist));
#endif
    return false;
  }

  if (MissingVolumes>FoundRecVolumes)
  {
#ifndef SILENT
    mprintf(St(MRecVolCannotFix));
#endif
    return false;
  }
#ifndef SILENT
  mprintf(St(MReconstructing));
#endif

  int TotalFiles=FileNumber+RecVolNumber;
  int Erasures[256],EraSize=0;

  for (int I=0;I<TotalFiles;I++)
    if (WriteFlags[I] || SrcFile[I]==NULL)
      Erasures[EraSize++]=I;

#ifndef SILENT
  int64 ProcessedSize=0;
#ifndef GUI
  int LastPercent=-1;
  mprintf(L"     ");
#endif
#endif
  // Size of per file buffer.
  size_t RecBufferSize=TotalBufferSize/TotalFiles;

#ifdef RAR_SMP
  uint ThreadNumber=Cmd->Threads;
  RSEncode rse[MaxPoolThreads];
#else
  uint ThreadNumber=1;
  RSEncode rse[1];
#endif
  for (uint I=0;I<ThreadNumber;I++)
    rse[I].Init(RecVolNumber);

  while (true)
  {
    Wait();
    int MaxRead=0;
    for (int I=0;I<TotalFiles;I++)
      if (WriteFlags[I] || SrcFile[I]==NULL)
        memset(&Buf[I*RecBufferSize],0,RecBufferSize);
      else
      {
        int ReadSize=SrcFile[I]->Read(&Buf[I*RecBufferSize],RecBufferSize);
        if ((size_t)ReadSize!=RecBufferSize)
          memset(&Buf[I*RecBufferSize+ReadSize],0,RecBufferSize-ReadSize);
        if (ReadSize>MaxRead)
          MaxRead=ReadSize;
      }
    if (MaxRead==0)
      break;
#ifndef SILENT
    int CurPercent=ToPercent(ProcessedSize,RecFileSize);
    if (!Cmd->DisablePercentage && CurPercent!=LastPercent)
    {
      mprintf(L"\b\b\b\b%3d%%",CurPercent);
      LastPercent=CurPercent;
    }
    ProcessedSize+=MaxRead;
#endif

    int BlockStart=0;
    int BlockSize=MaxRead/ThreadNumber;
    if (BlockSize<0x100)
      BlockSize=MaxRead;
    
    for (uint CurThread=0;BlockStart<MaxRead;CurThread++)
    {
      // Last thread processes all left data including increasement
      // from rounding error.
      if (CurThread==ThreadNumber-1)
        BlockSize=MaxRead-BlockStart;

      RSEncode *curenc=rse+CurThread;
      curenc->Buf=&Buf[0];
      curenc->BufStart=BlockStart;
      curenc->BufEnd=BlockStart+BlockSize;
      curenc->FileNumber=TotalFiles;
      curenc->RecBufferSize=RecBufferSize;
      curenc->Erasures=Erasures;
      curenc->EraSize=EraSize;

#ifdef RAR_SMP
      if (ThreadNumber>1)
        RSThreadPool->AddTask(RSDecodeThread,(void*)curenc);
      else
        curenc->DecodeBuf();
#else
      curenc->DecodeBuf();
#endif

      BlockStart+=BlockSize;
    }

#ifdef RAR_SMP
    RSThreadPool->WaitDone();
#endif // RAR_SMP
    
    for (int I=0;I<FileNumber;I++)
      if (WriteFlags[I])
        SrcFile[I]->Write(&Buf[I*RecBufferSize],MaxRead);
  }
  for (int I=0;I<RecVolNumber+FileNumber;I++)
    if (SrcFile[I]!=NULL)
    {
      File *CurFile=SrcFile[I];
      if (NewStyle && WriteFlags[I])
      {
        int64 Length=CurFile->Tell();
        CurFile->Seek(Length-7,SEEK_SET);
        for (int J=0;J<7;J++)
          CurFile->PutByte(0);
      }
      CurFile->Close();
      SrcFile[I]=NULL;
    }
  if (*LastVolName!=0)
  {
    // Truncate the last volume to its real size.
    Archive Arc(Cmd);
    if (Arc.Open(LastVolName,FMF_UPDATE) && Arc.IsArchive(true) &&
        Arc.SearchBlock(HEAD_ENDARC))
    {
      Arc.Seek(Arc.NextBlockPos,SEEK_SET);
      char Buf[8192];
      int ReadSize=Arc.Read(Buf,sizeof(Buf));
      int ZeroCount=0;
      while (ZeroCount<ReadSize && Buf[ZeroCount]==0)
        ZeroCount++;
      if (ZeroCount==ReadSize)
      {
        Arc.Seek(Arc.NextBlockPos,SEEK_SET);
        Arc.Truncate();
      }
    }
  }
#if !defined(GUI) && !defined(SILENT)
  if (!Cmd->DisablePercentage)
    mprintf(L"\b\b\b\b100%%");
  if (!Silent && !Cmd->DisableDone)
    mprintf(St(MDone));
#endif
  return true;
}