// We should return 'true' even if resulting path is shorter than MAX_PATH,
// because we can also use this function to open files with non-standard
// characters, even if their path length is normal.
bool GetWinLongPath(const wchar *Src,wchar *Dest,size_t MaxSize)
{
if (*Src==0)
return false;
const wchar *Prefix=L"\\\\?\\";
const size_t PrefixLength=4;
bool FullPath=IsDiskLetter(Src) && IsPathDiv(Src[2]);
size_t SrcLength=wcslen(Src);
if (IsFullPath(Src)) // Paths in d:\path\name format.
{
if (IsDiskLetter(Src))
{
if (MaxSize<=PrefixLength+SrcLength)
return false;
wcsncpy(Dest,Prefix,PrefixLength);
wcscpy(Dest+PrefixLength,Src);
return true;
}
else
if (Src[0]=='\\' && Src[1]=='\\')
{
if (MaxSize<=PrefixLength+SrcLength+2)
return false;
wcsncpy(Dest,Prefix,PrefixLength);
wcscpy(Dest+PrefixLength,L"UNC");
wcscpy(Dest+PrefixLength+3,Src+1);
return true;
}
// We may be here only if we modify IsFullPath in the future.
return false;
}
else
{
wchar CurDir[NM];
DWORD DirCode=GetCurrentDirectory(ASIZE(CurDir)-1,CurDir);
if (DirCode==0 || DirCode>ASIZE(CurDir)-1)
return false;
if (IsPathDiv(Src[0])) // Paths in \path\name format.
{
if (MaxSize<=PrefixLength+SrcLength+2)
return false;
wcsncpy(Dest,Prefix,PrefixLength);
wcsncpy(Dest+PrefixLength,CurDir,2); // Copy drive letter 'd:'.
wcscpy(Dest+PrefixLength+2,Src);
return true;
}
else // Paths in path\name format.
{
AddEndSlash(CurDir,ASIZE(CurDir));
if (MaxSize<=PrefixLength+wcslen(CurDir)+SrcLength)
return false;
wcsncpy(Dest,Prefix,PrefixLength);
wcscpy(Dest+PrefixLength,CurDir);
if (Src[0]=='.' && IsPathDiv(Src[1])) // Remove leading .\ in pathname.
Src+=2;
wcsncatz(Dest,Src,MaxSize);
return true;
}
}
return false;
}
bool ScanTree::GetNextMask()
{
if (!FileMasks->GetString(CurMask,CurMaskW,ASIZE(CurMask)))
return(false);
if (*CurMask==0 && *CurMaskW!=0)
{
// Unicode only mask is present. It is very unlikely in console tools,
// but possible if called from GUI WinRAR. We still need to have
// ASCII mask, because we use ASCII only mask in some checks later.
// So let's convert Unicode to ASCII.
WideToChar(CurMaskW,CurMask,ASIZE(CurMask));
}
CurMask[ASIZE(CurMask)-1]=0;
CurMaskW[ASIZE(CurMaskW)-1]=0;
#ifdef _WIN_ALL
UnixSlashToDos(CurMask);
#endif
// We wish to scan entire disk if mask like c:\ is specified
// regardless of recursion mode. Use c:\*.* mask when need to scan only
// the root directory.
ScanEntireDisk=IsDiskLetter(CurMask) && IsPathDiv(CurMask[2]) && CurMask[3]==0;
char *Name=PointToName(CurMask);
if (*Name==0)
strcat(CurMask,MASKALL);
if (Name[0]=='.' && (Name[1]==0 || Name[1]=='.' && Name[2]==0))
{
AddEndSlash(CurMask);
strcat(CurMask,MASKALL);
}
SpecPathLength=Name-CurMask;
bool WideName=(*CurMaskW!=0);
if (WideName)
{
wchar *NameW=PointToName(CurMaskW);
if (*NameW==0)
wcscat(CurMaskW,MASKALLW);
if (NameW[0]=='.' && (NameW[1]==0 || NameW[1]=='.' && NameW[2]==0))
{
AddEndSlash(CurMaskW);
wcscat(CurMaskW,MASKALLW);
}
SpecPathLengthW=NameW-CurMaskW;
}
else
{
wchar WideMask[NM];
CharToWide(CurMask,WideMask);
SpecPathLengthW=PointToName(WideMask)-WideMask;
}
Depth=0;
strcpy(OrigCurMask,CurMask);
wcscpy(OrigCurMaskW,CurMaskW);
return(true);
}
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;
}
//.........这里部分代码省略.........
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
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,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
//.........这里部分代码省略.........
// 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);
}
bool FileCreate(RAROptions *Cmd,File *NewFile,char *Name,wchar *NameW,
OVERWRITE_MODE Mode,bool *UserReject,int64 FileSize,
uint FileTime,bool WriteOnly)
{
if (UserReject!=NULL)
*UserReject=false;
#if defined(_WIN_ALL) && !defined(_WIN_CE)
bool ShortNameChanged=false;
#endif
while (FileExist(Name,NameW))
{
#if defined(_WIN_ALL) && !defined(_WIN_CE)
if (!ShortNameChanged)
{
// Avoid the infinite loop if UpdateExistingShortName returns
// the same name.
ShortNameChanged=true;
// Maybe our long name matches the short name of existing file.
// Let's check if we can change the short name.
wchar WideName[NM];
GetWideName(Name,NameW,WideName,ASIZE(WideName));
if (UpdateExistingShortName(WideName))
{
if (Name!=NULL && *Name!=0)
WideToChar(WideName,Name);
if (NameW!=NULL && *NameW!=0)
wcscpy(NameW,WideName);
continue;
}
}
// Allow short name check again. It is necessary, because rename and
// autorename below can change the name, so we need to check it again.
ShortNameChanged=false;
#endif
if (Mode==OVERWRITE_NONE)
{
if (UserReject!=NULL)
*UserReject=true;
return(false);
}
// Must be before Cmd->AllYes check or -y switch would override -or.
if (Mode==OVERWRITE_AUTORENAME)
{
if (!GetAutoRenamedName(Name,NameW))
Mode=OVERWRITE_DEFAULT;
continue;
}
#ifdef SILENT
Mode=OVERWRITE_ALL;
#endif
// This check must be after OVERWRITE_AUTORENAME processing or -y switch
// would override -or.
if (Cmd->AllYes || Mode==OVERWRITE_ALL)
break;
if (Mode==OVERWRITE_DEFAULT || Mode==OVERWRITE_FORCE_ASK)
{
char NewName[NM];
wchar NewNameW[NM];
*NewNameW=0;
eprintf(St(MFileExists),Name);
int Choice=Ask(St(MYesNoAllRenQ));
if (Choice==1)
break;
if (Choice==2)
{
if (UserReject!=NULL)
*UserReject=true;
return(false);
}
if (Choice==3)
{
Cmd->Overwrite=OVERWRITE_ALL;
break;
}
if (Choice==4)
{
if (UserReject!=NULL)
*UserReject=true;
Cmd->Overwrite=OVERWRITE_NONE;
return(false);
}
if (Choice==5)
{
#ifndef GUI
mprintf(St(MAskNewName));
#ifdef _WIN_ALL
File SrcFile;
SrcFile.SetHandleType(FILE_HANDLESTD);
int Size=SrcFile.Read(NewName,sizeof(NewName)-1);
NewName[Size]=0;
OemToCharA(NewName,NewName);
#else
if (fgets(NewName,sizeof(NewName),stdin)==NULL)
{
//.........这里部分代码省略.........
请发表评论