bool GetAutoRenamedName(wchar *Name,size_t MaxNameSize) { wchar NewName[NM]; size_t NameLength=wcslen(Name); #ifdef _ANDROID if (NameLength>ASIZE(NewName)-10) return false; #endif wchar *Ext=GetExt(Name); if (Ext==NULL) Ext=Name+NameLength; for (uint FileVer=1;;FileVer++) { #ifdef _ANDROID // No swprintf in Android NDK r9. uint NamePrefixLength=Ext-Name; wcsncpy(NewName,Name,NamePrefixLength); wcscpy(NewName+NamePrefixLength,L"("); itoa(FileVer,NewName+NamePrefixLength+1,ASIZE(NewName)-NamePrefixLength-1); wcsncatz(NewName,L")",ASIZE(NewName)); wcsncatz(NewName,Ext,ASIZE(NewName)); #else swprintf(NewName,ASIZE(NewName),L"%.*ls(%u)%ls",uint(Ext-Name),Name,FileVer,Ext); #endif if (!FileExist(NewName)) { wcsncpyz(Name,NewName,MaxNameSize); break; } if (FileVer>=1000000) return false; } return true; }
bool ScanTree::GetNextMask() { if (!GetFilteredMask()) return false; #ifdef _WIN_ALL UnixSlashToDos(CurMask,CurMask,ASIZE(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=IsDriveLetter(CurMask) && IsPathDiv(CurMask[2]) && CurMask[3]==0; wchar *Name=PointToName(CurMask); if (*Name==0) wcsncatz(CurMask,MASKALL,ASIZE(CurMask)); if (Name[0]=='.' && (Name[1]==0 || Name[1]=='.' && Name[2]==0)) { AddEndSlash(CurMask,ASIZE(CurMask)); wcsncatz(CurMask,MASKALL,ASIZE(CurMask)); } SpecPathLength=Name-CurMask; Depth=0; wcsncpyz(OrigCurMask,CurMask,ASIZE(OrigCurMask)); return true; }
void CommandData::ProcessCommand() { #ifndef SFX_MODULE const wchar *SingleCharCommands=L"FUADPXETK"; if (Command[0]!=0 && Command[1]!=0 && wcschr(SingleCharCommands,Command[0])!=NULL || *ArcName==0) OutHelp(*Command==0 ? RARX_SUCCESS:RARX_USERERROR); // Return 'success' for 'rar' without parameters. #ifdef _UNIX if (GetExt(ArcName)==NULL && (!FileExist(ArcName) || IsDir(GetFileAttr(ArcName)))) wcsncatz(ArcName,L".rar",ASIZE(ArcName)); #else if (GetExt(ArcName)==NULL) wcsncatz(ArcName,L".rar",ASIZE(ArcName)); #endif if (wcschr(L"AFUMD",*Command)==NULL) { if (GenerateArcName) GenerateArchiveName(ArcName,ASIZE(ArcName),GenerateMask,false); StringList ArcMasks; ArcMasks.AddString(ArcName); ScanTree Scan(&ArcMasks,Recurse,SaveSymLinks,SCAN_SKIPDIRS); FindData FindData; while (Scan.GetNext(&FindData)==SCAN_SUCCESS) AddArcName(FindData.Name); } else AddArcName(ArcName); #endif switch(Command[0]) { case 'P': case 'X': case 'E': case 'T': case 'I': { CmdExtract Extract(this); Extract.DoExtract(); } break; #ifndef SILENT case 'V': case 'L': ListArchive(this); break; default: OutHelp(RARX_USERERROR); #endif } if (!BareOutput) mprintf(L"\n"); }
void SetExt(wchar *Name,const wchar *NewExt,size_t MaxSize) { if (Name==NULL || *Name==0) return; wchar *Dot=GetExt(Name); if (Dot!=NULL) *Dot=0; if (NewExt!=NULL) { wcsncatz(Name,L".",MaxSize); wcsncatz(Name,NewExt,MaxSize); } }
// 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) { 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); wcsncatz(Dest,Src,MaxSize); return true; } } return false; }
void MakeName(const wchar *Path,const wchar *Name,wchar *Pathname,size_t MaxSize) { // 'Name' and 'Pathname' can point to same memory area. This is why we use // the temporary buffer instead of constructing the name in 'Pathname'. wchar OutName[NM]; wcsncpyz(OutName,Path,ASIZE(OutName)); AddEndSlash(OutName,ASIZE(OutName)); wcsncatz(OutName,Name,ASIZE(OutName)); wcsncpyz(Pathname,OutName,MaxSize); }
void GetConfigName(const wchar *Name,wchar *FullName,size_t MaxSize,bool CheckExist,bool Create) { *FullName=0; for (uint I=0;EnumConfigPaths(I,FullName,MaxSize,Create);I++) { AddEndSlash(FullName,MaxSize); wcsncatz(FullName,Name,MaxSize); if (!CheckExist || WildFileExist(FullName)) break; } }
void CommandData::OutTitle() { if (BareOutput || DisableCopyright) return; #if defined(__GNUC__) && defined(SFX_MODULE) mprintf(St(MCopyrightS)); #else #ifndef SILENT static bool TitleShown=false; if (TitleShown) return; TitleShown=true; wchar Version[80]; if (RARVER_BETA!=0) swprintf(Version,ASIZE(Version),L"%d.%02d %ls %d",RARVER_MAJOR,RARVER_MINOR,St(MBeta),RARVER_BETA); else swprintf(Version,ASIZE(Version),L"%d.%02d",RARVER_MAJOR,RARVER_MINOR); #if defined(_WIN_32) || defined(_WIN_64) wcsncatz(Version,L" ",ASIZE(Version)); #endif #ifdef _WIN_32 wcsncatz(Version,St(Mx86),ASIZE(Version)); #endif #ifdef _WIN_64 wcsncatz(Version,St(Mx64),ASIZE(Version)); #endif if (PrintVersion) { mprintf(L"%s",Version); exit(0); } #ifdef UNRAR mprintf(St(MUCopyright),Version,RARVER_YEAR); #else #endif #endif #endif }
void ExtractStreams(Archive &Arc,const wchar *FileName,bool TestMode) { wchar FullName[NM+2]; if (FileName[0]!=0 && FileName[1]==0) { wcscpy(FullName,L".\\"); wcsncpyz(FullName+2,FileName,ASIZE(FullName)-2); } else wcsncpyz(FullName,FileName,ASIZE(FullName)); byte *Data=&Arc.SubHead.SubData[0]; size_t DataSize=Arc.SubHead.SubData.Size(); wchar StreamName[NM]; GetStreamNameNTFS(Arc,StreamName,ASIZE(StreamName)); if (*StreamName!=':') { uiMsg(UIERROR_STREAMBROKEN,Arc.FileName,FileName); ErrHandler.SetErrorCode(RARX_CRC); return; } if (TestMode) { Arc.ReadSubData(NULL,NULL); return; } wcsncatz(FullName,StreamName,ASIZE(FullName)); FindData fd; bool Found=FindFile::FastFind(FileName,&fd); if ((fd.FileAttr & FILE_ATTRIBUTE_READONLY)!=0) SetFileAttr(FileName,fd.FileAttr & ~FILE_ATTRIBUTE_READONLY); File CurFile; if (CurFile.WCreate(FullName) && Arc.ReadSubData(NULL,&CurFile)) CurFile.Close(); File HostFile; if (Found && HostFile.Open(FileName,FMF_OPENSHARED|FMF_UPDATE)) SetFileTime(HostFile.GetHandle(),&fd.ftCreationTime,&fd.ftLastAccessTime, &fd.ftLastWriteTime); // Restoring original file attributes. Important if file was read only // or did not have "Archive" attribute SetFileAttr(FileName,fd.FileAttr); }
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--; } } }
// For masks like dir1\dir2*\*.ext in non-recursive mode. bool ScanTree::ExpandFolderMask() { bool WildcardFound=false; uint SlashPos=0; for (int I=0;CurMask[I]!=0;I++) { if (CurMask[I]=='?' || CurMask[I]=='*') WildcardFound=true; if (WildcardFound && IsPathDiv(CurMask[I])) { // First path separator position after folder wildcard mask. // In case of dir1\dir2*\dir3\name.ext mask it may point not to file // name, so we cannot use PointToName() here. SlashPos=I; break; } } wchar Mask[NM]; wcsncpyz(Mask,CurMask,ASIZE(Mask)); Mask[SlashPos]=0; // Prepare the list of all folders matching the wildcard mask. ExpandedFolderList.Reset(); FindFile Find; Find.SetMask(Mask); FindData FD; while (Find.Next(&FD)) if (FD.IsDir) { wcsncatz(FD.Name,CurMask+SlashPos,ASIZE(FD.Name)); // Treat dir*\* or dir*\*.* as dir, so empty 'dir' is also matched // by such mask. Skipping empty dir with dir*\*.* confused some users. wchar *LastMask=PointToName(FD.Name); if (wcscmp(LastMask,L"*")==0 || wcscmp(LastMask,L"*.*")==0) RemoveNameFromPath(FD.Name); ExpandedFolderList.AddString(FD.Name); } if (ExpandedFolderList.ItemsCount()==0) return false; // Return the first matching folder name now. ExpandedFolderList.GetString(CurMask,ASIZE(CurMask)); return true; }
bool GetAppDataPath(wchar *Path,size_t MaxSize,bool Create) { LPMALLOC g_pMalloc; SHGetMalloc(&g_pMalloc); LPITEMIDLIST ppidl; *Path=0; bool Success=false; if (SHGetSpecialFolderLocation(NULL,CSIDL_APPDATA,&ppidl)==NOERROR && SHGetPathFromIDList(ppidl,Path) && *Path!=0) { AddEndSlash(Path,MaxSize); wcsncatz(Path,L"WinRAR",MaxSize); Success=FileExist(Path); if (!Success && Create) Success=MakeDir(Path,false,0)==MKDIR_SUCCESS; } g_pMalloc->Free(ppidl); return Success; }
static void GetAppDataPath(wchar *Path,size_t MaxSize) { LPMALLOC g_pMalloc; SHGetMalloc(&g_pMalloc); LPITEMIDLIST ppidl; *Path=0; bool Success=false; if (SHGetSpecialFolderLocation(NULL,CSIDL_APPDATA,&ppidl)==NOERROR && SHGetPathFromIDList(ppidl,Path) && *Path!=0) { AddEndSlash(Path,MaxSize); wcsncatz(Path,L"WinRAR",MaxSize); Success=FileExist(Path) || MakeDir(Path,false,0)==MKDIR_SUCCESS; } if (!Success) { GetModuleFileName(NULL,Path,(DWORD)MaxSize); RemoveNameFromPath(Path); } g_pMalloc->Free(ppidl); }
void ConvertNameToFull(const wchar *Src,wchar *Dest,size_t MaxSize) { if (Src==NULL || *Src==0) { if (MaxSize>0) *Dest=0; return; } #ifdef _WIN_ALL { wchar FullName[NM],*NamePtr; DWORD Code=GetFullPathName(Src,ASIZE(FullName),FullName,&NamePtr); if (Code==0 || Code>ASIZE(FullName)) { wchar LongName[NM]; if (GetWinLongPath(Src,LongName,ASIZE(LongName))) Code=GetFullPathName(LongName,ASIZE(FullName),FullName,&NamePtr); } if (Code!=0 && Code<ASIZE(FullName)) wcsncpyz(Dest,FullName,MaxSize); else if (Src!=Dest) wcsncpyz(Dest,Src,MaxSize); } #elif defined(_UNIX) if (IsFullPath(Src)) *Dest=0; else { char CurDirA[NM]; if (getcwd(CurDirA,ASIZE(CurDirA))==NULL) *CurDirA=0; CharToWide(CurDirA,Dest,MaxSize); AddEndSlash(Dest,MaxSize); } wcsncatz(Dest,Src,MaxSize); #else wcsncpyz(Dest,Src,MaxSize); #endif }
SCAN_CODE ScanTree::FindProc(FindData *FD) { if (*CurMask==0) return SCAN_NEXT; bool FastFindFile=false; if (FindStack[Depth]==NULL) // No FindFile object for this depth yet. { bool Wildcards=IsWildcard(CurMask); // If we have a file name without wildcards, we can try to use // FastFind to optimize speed. For example, in Unix it results in // stat call instead of opendir/readdir/closedir. bool FindCode=!Wildcards && FindFile::FastFind(CurMask,FD,GetLinks); // Link check is important for NTFS, where links can have "Directory" // attribute, but we do not want to recurse to them in "get links" mode. bool IsDir=FindCode && FD->IsDir && (!GetLinks || !FD->IsLink); // SearchAll means that we'll use "*" mask for search, so we'll find // subdirectories and will be able to recurse into them. // We do not use "*" for directories at any level or for files // at top level in recursion mode. We always comrpess the entire directory // if folder wildcard is specified. bool SearchAll=!IsDir && (Depth>0 || Recurse==RECURSE_ALWAYS || FolderWildcards && Recurse!=RECURSE_DISABLE || Wildcards && Recurse==RECURSE_WILDCARDS || ScanEntireDisk && Recurse!=RECURSE_DISABLE); if (Depth==0) SearchAllInRoot=SearchAll; if (SearchAll || Wildcards) { // Create the new FindFile object for wildcard based search. FindStack[Depth]=new FindFile; wchar SearchMask[NM]; wcsncpyz(SearchMask,CurMask,ASIZE(SearchMask)); if (SearchAll) SetName(SearchMask,MASKALL,ASIZE(SearchMask)); FindStack[Depth]->SetMask(SearchMask); } else { // Either we failed to fast find or we found a file or we found // a directory in RECURSE_DISABLE mode, so we do not need to scan it. // We can return here and do not need to process further. // We need to process further only if we fast found a directory. if (!FindCode || !IsDir || Recurse==RECURSE_DISABLE) { // Return SCAN_SUCCESS if we found a file. SCAN_CODE RetCode=SCAN_SUCCESS; if (!FindCode) { // Return SCAN_ERROR if problem is more serious than just // "file not found". RetCode=FD->Error ? SCAN_ERROR:SCAN_NEXT; // If we failed to find an object, but our current mask is excluded, // we skip this object and avoid indicating an error. if (Cmd!=NULL && Cmd->ExclCheck(CurMask,false,true,true)) RetCode=SCAN_NEXT; else { ErrHandler.OpenErrorMsg(ErrArcName,CurMask); // User asked to return RARX_NOFILES and not RARX_OPEN here. ErrHandler.SetErrorCode(RARX_NOFILES); } } // If we searched only for one file or directory in "fast find" // (without a wildcard) mode, let's set masks to zero, // so calling function will know that current mask is used // and next one must be read from mask list for next call. // It is not necessary for directories, because even in "fast find" // mode, directory recursing will quit by (Depth < 0) condition, // which returns SCAN_DONE to calling function. *CurMask=0; return RetCode; } // We found a directory using only FindFile::FastFind function. FastFindFile=true; } } if (!FastFindFile && !FindStack[Depth]->Next(FD,GetLinks)) { // We cannot find anything more in directory either because of // some error or just as result of all directory entries already read. bool Error=FD->Error; if (Error) ScanError(Error); wchar DirName[NM]; *DirName=0; // Going to at least one directory level higher. delete FindStack[Depth]; FindStack[Depth--]=NULL; while (Depth>=0 && FindStack[Depth]==NULL) Depth--; if (Depth < 0) { // Directories scanned both in normal and FastFindFile mode, // finally exit from scan here, by (Depth < 0) condition. if (Error) Errors++; return SCAN_DONE; } wchar *Slash=wcsrchr(CurMask,CPATHDIVIDER); if (Slash!=NULL) { wchar Mask[NM]; wcsncpyz(Mask,Slash,ASIZE(Mask)); if (Depth<SetAllMaskDepth) wcsncpyz(Mask+1,PointToName(OrigCurMask),ASIZE(Mask)-1); *Slash=0; wcsncpyz(DirName,CurMask,ASIZE(DirName)); wchar *PrevSlash=wcsrchr(CurMask,CPATHDIVIDER); if (PrevSlash==NULL) wcsncpyz(CurMask,Mask+1,ASIZE(CurMask)); else { *(PrevSlash+1)=0; wcsncatz(CurMask,Mask,ASIZE(CurMask)); } } if (GetDirs==SCAN_GETDIRSTWICE && FindFile::FastFind(DirName,FD,GetLinks) && FD->IsDir) { FD->Flags|=FDDF_SECONDDIR; return Error ? SCAN_ERROR:SCAN_SUCCESS; } return Error ? SCAN_ERROR:SCAN_NEXT; } // Link check is required for NTFS links, not for Unix. if (FD->IsDir && (!GetLinks || !FD->IsLink)) { // If we found the directory in top (Depth==0) directory // and if we are not in "fast find" (directory name only as argument) // or in recurse (SearchAll was set when opening the top directory) mode, // we do not recurse into this directory. We either return it by itself // or skip it. if (!FastFindFile && Depth==0 && !SearchAllInRoot) return GetDirs==SCAN_GETCURDIRS ? SCAN_SUCCESS:SCAN_NEXT; // Let's check if directory name is excluded, so we do not waste // time searching in directory, which will be excluded anyway. if (Cmd!=NULL && (Cmd->ExclCheck(FD->Name,true,false,false) || Cmd->ExclDirByAttr(FD->FileAttr))) { // If we are here in "fast find" mode, it means that entire directory // specified in command line is excluded. Then we need to return // SCAN_DONE to go to next mask and avoid the infinite loop // in GetNext() function. Such loop would be possible in case of // SCAN_NEXT code and "rar a arc dir -xdir" command. return FastFindFile ? SCAN_DONE:SCAN_NEXT; } wchar Mask[NM]; wcsncpyz(Mask,FastFindFile ? MASKALL:PointToName(CurMask),ASIZE(Mask)); wcsncpyz(CurMask,FD->Name,ASIZE(CurMask)); if (wcslen(CurMask)+wcslen(Mask)+1>=NM || Depth>=MAXSCANDEPTH-1) { uiMsg(UIERROR_PATHTOOLONG,CurMask,SPATHDIVIDER,Mask); return SCAN_ERROR; } AddEndSlash(CurMask,ASIZE(CurMask)); wcsncatz(CurMask,Mask,ASIZE(CurMask)); Depth++; // We need to use OrigCurMask for depths less than SetAllMaskDepth // and "*" for depths equal or larger than SetAllMaskDepth. // It is important when "fast finding" directories at Depth > 0. // For example, if current directory is RootFolder and we compress // the following directories structure: // RootFolder // +--Folder1 // | +--Folder2 // | +--Folder3 // +--Folder4 // with 'rar a -r arcname Folder2' command, rar could add not only // Folder1\Folder2 contents, but also Folder1\Folder3 if we were using // "*" mask at all levels. We need to use "*" mask inside of Folder2, // but return to "Folder2" mask when completing scanning Folder2. // We can rewrite SearchAll expression above to avoid fast finding // directories at Depth > 0, but then 'rar a -r arcname Folder2' // will add the empty Folder2 and do not add its contents. if (FastFindFile) SetAllMaskDepth=Depth; } if (!FastFindFile && !CmpName(CurMask,FD->Name,MATCH_NAMES)) return SCAN_NEXT; return SCAN_SUCCESS; }
void ExtractStreams20(Archive &Arc,const wchar *FileName) { if (Arc.BrokenHeader) { uiMsg(UIERROR_STREAMBROKEN,Arc.FileName,FileName); ErrHandler.SetErrorCode(RARX_CRC); return; } if (Arc.StreamHead.Method<0x31 || Arc.StreamHead.Method>0x35 || Arc.StreamHead.UnpVer>VER_PACK) { uiMsg(UIERROR_STREAMUNKNOWN,Arc.FileName,FileName); ErrHandler.SetErrorCode(RARX_WARNING); return; } wchar StreamName[NM+2]; if (FileName[0]!=0 && FileName[1]==0) { wcscpy(StreamName,L".\\"); wcscpy(StreamName+2,FileName); } else wcscpy(StreamName,FileName); if (wcslen(StreamName)+strlen(Arc.StreamHead.StreamName)>=ASIZE(StreamName) || Arc.StreamHead.StreamName[0]!=':') { uiMsg(UIERROR_STREAMBROKEN,Arc.FileName,FileName); ErrHandler.SetErrorCode(RARX_CRC); return; } wchar StoredName[NM]; CharToWide(Arc.StreamHead.StreamName,StoredName,ASIZE(StoredName)); ConvertPath(StoredName+1,StoredName+1); wcsncatz(StreamName,StoredName,ASIZE(StreamName)); FindData fd; bool Found=FindFile::FastFind(FileName,&fd); if ((fd.FileAttr & FILE_ATTRIBUTE_READONLY)!=0) SetFileAttr(FileName,fd.FileAttr & ~FILE_ATTRIBUTE_READONLY); File CurFile; if (CurFile.WCreate(StreamName)) { ComprDataIO DataIO; Unpack Unpack(&DataIO); Unpack.Init(0x10000,false); DataIO.SetPackedSizeToRead(Arc.StreamHead.DataSize); DataIO.EnableShowProgress(false); DataIO.SetFiles(&Arc,&CurFile); DataIO.UnpHash.Init(HASH_CRC32,1); Unpack.SetDestSize(Arc.StreamHead.UnpSize); Unpack.DoUnpack(Arc.StreamHead.UnpVer,false); if (Arc.StreamHead.StreamCRC!=DataIO.UnpHash.GetCRC32()) { uiMsg(UIERROR_STREAMBROKEN,Arc.FileName,StreamName); ErrHandler.SetErrorCode(RARX_CRC); } else CurFile.Close(); } File HostFile; if (Found && HostFile.Open(FileName,FMF_OPENSHARED|FMF_UPDATE)) SetFileTime(HostFile.GetHandle(),&fd.ftCreationTime,&fd.ftLastAccessTime, &fd.ftLastWriteTime); if ((fd.FileAttr & FILE_ATTRIBUTE_READONLY)!=0) SetFileAttr(FileName,fd.FileAttr); }
// For masks like dir1\dir2*\file.ext this function sets 'dir1' recursive mask // and '*\dir2*\file.ext' filter. Masks without folder wildcards are // returned as is. bool ScanTree::GetFilteredMask() { // If we have some matching folders left for non-recursive folder wildcard // mask, we return it here. if (ExpandedFolderList.ItemsCount()>0 && ExpandedFolderList.GetString(CurMask,ASIZE(CurMask))) return true; FolderWildcards=false; FilterList.Reset(); if (!FileMasks->GetString(CurMask,ASIZE(CurMask))) return false; // Check if folder wildcards present. bool WildcardFound=false; uint FolderWildcardCount=0; uint SlashPos=0; for (int I=0;CurMask[I]!=0;I++) { if (CurMask[I]=='?' || CurMask[I]=='*') WildcardFound=true; if (IsPathDiv(CurMask[I]) || IsDriveDiv(CurMask[I])) { if (WildcardFound) { // Calculate a number of folder wildcards in current mask. FolderWildcardCount++; WildcardFound=false; } if (FolderWildcardCount==0) SlashPos=I; // Slash position before first folder wildcard mask. } } if (FolderWildcardCount==0) return true; FolderWildcards=true; // Global folder wildcards flag. // If we have only one folder wildcard component and -r is missing or -r- // is specified, prepare matching folders in non-recursive mode. // We assume -r for masks like dir1*\dir2*\file*, because it is complicated // to fast find them using OS file find API call. if ((Recurse==RECURSE_NONE || Recurse==RECURSE_DISABLE) && FolderWildcardCount==1) return ExpandFolderMask(); wchar Filter[NM]; // Convert path\dir*\ to *\dir filter to search for 'dir' in all 'path' subfolders. wcscpy(Filter,L"*"); AddEndSlash(Filter,ASIZE(Filter)); // SlashPos might point or not point to path separator for masks like 'dir*', '\dir*' or 'd:dir*' wchar *WildName=IsPathDiv(CurMask[SlashPos]) || IsDriveDiv(CurMask[SlashPos]) ? CurMask+SlashPos+1 : CurMask+SlashPos; wcsncatz(Filter,WildName,ASIZE(Filter)); // Treat dir*\* or dir*\*.* as dir\, so empty 'dir' is also matched // by such mask. Skipping empty dir with dir*\*.* confused some users. wchar *LastMask=PointToName(Filter); if (wcscmp(LastMask,L"*")==0 || wcscmp(LastMask,L"*.*")==0) *LastMask=0; FilterList.AddString(Filter); bool RelativeDrive=IsDriveDiv(CurMask[SlashPos]); if (RelativeDrive) SlashPos++; // Use "d:" instead of "d" for d:* mask. CurMask[SlashPos]=0; if (!RelativeDrive) // Keep d: mask as is, not convert to d:\* { // We need to append "\*" both for -ep1 to work correctly and to // convert d:\* masks previously truncated to d: back to original form. AddEndSlash(CurMask,ASIZE(CurMask)); wcsncatz(CurMask,MASKALL,ASIZE(CurMask)); } return true; }
void CmdExtract::ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *DestName,size_t DestSize) { wcsncpyz(DestName,Cmd->ExtrPath,DestSize); // We need IsPathDiv check here to correctly handle Unix forward slash // in the end of destination path in Windows: rar x arc dest/ if (*Cmd->ExtrPath!=0 && !IsPathDiv(*PointToLastChar(Cmd->ExtrPath))) { // Destination path can be without trailing slash if it come from GUI shell. AddEndSlash(DestName,DestSize); } #ifndef SFX_MODULE if (Cmd->AppendArcNameToPath) { wcsncatz(DestName,PointToName(Arc.FirstVolumeName),DestSize); SetExt(DestName,NULL,DestSize); AddEndSlash(DestName,DestSize); } #endif #ifndef SFX_MODULE size_t ArcPathLength=wcslen(Cmd->ArcPath); if (ArcPathLength>0) { size_t NameLength=wcslen(ArcFileName); ArcFileName+=Min(ArcPathLength,NameLength); while (*ArcFileName==CPATHDIVIDER) ArcFileName++; if (*ArcFileName==0) // Excessive -ap switch. { *DestName=0; return; } } #endif wchar Command=Cmd->Command[0]; // Use -ep3 only in systems, where disk letters are exist, not in Unix. bool AbsPaths=Cmd->ExclPath==EXCL_ABSPATH && Command=='X' && IsDriveDiv(':'); // We do not use any user specified destination paths when extracting // absolute paths in -ep3 mode. if (AbsPaths) *DestName=0; if (Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH) wcsncatz(DestName,PointToName(ArcFileName),DestSize); else wcsncatz(DestName,ArcFileName,DestSize); wchar DiskLetter=toupperw(DestName[0]); if (AbsPaths) { if (DestName[1]=='_' && IsPathDiv(DestName[2]) && DiskLetter>='A' && DiskLetter<='Z') DestName[1]=':'; else if (DestName[0]=='_' && DestName[1]=='_') { // Convert __server\share to \\server\share. DestName[0]=CPATHDIVIDER; DestName[1]=CPATHDIVIDER; } } }
bool CommandData::CheckArgs(StringList *Args,bool Dir,const wchar *CheckName,bool CheckFullPath,int MatchMode) { wchar *Name=ConvertPath(CheckName,NULL); wchar FullName[NM]; wchar CurMask[NM]; *FullName=0; Args->Rewind(); while (Args->GetString(CurMask,ASIZE(CurMask))) { wchar *LastMaskChar=PointToLastChar(CurMask); bool DirMask=IsPathDiv(*LastMaskChar); // Mask for directories only. if (Dir) { // CheckName is a directory. if (DirMask) { // We process the directory and have the directory exclusion mask. // So let's convert "mask\" to "mask" and process it normally. *LastMaskChar=0; } else { // REMOVED, we want -npath\* to match empty folders too. // If mask has wildcards in name part and does not have the trailing // '\' character, we cannot use it for directories. // if (IsWildcard(PointToName(CurMask))) // continue; } } else { // If we process a file inside of directory excluded by "dirmask\". // we want to exclude such file too. So we convert "dirmask\" to // "dirmask\*". It is important for operations other than archiving // with -x. When archiving with -x, directory matched by "dirmask\" // is excluded from further scanning. if (DirMask) wcsncatz(CurMask,L"*",ASIZE(CurMask)); } #ifndef SFX_MODULE if (CheckFullPath && IsFullPath(CurMask)) { // We do not need to do the special "*\" processing here, because // unlike the "else" part of this "if", now we convert names to full // format, so they all include the path, which is matched by "*\" // correctly. Moreover, removing "*\" from mask would break // the comparison, because now all names have the path. if (*FullName==0) ConvertNameToFull(CheckName,FullName,ASIZE(FullName)); if (CmpName(CurMask,FullName,MatchMode)) return true; } else #endif { wchar NewName[NM+2],*CurName=Name; // Important to convert before "*\" check below, so masks like // d:*\something are processed properly. wchar *CmpMask=ConvertPath(CurMask,NULL); if (CmpMask[0]=='*' && IsPathDiv(CmpMask[1])) { // We want "*\name" to match 'name' not only in subdirectories, // but also in the current directory. We convert the name // from 'name' to '.\name' to be matched by "*\" part even if it is // in current directory. NewName[0]='.'; NewName[1]=CPATHDIVIDER; wcsncpyz(NewName+2,Name,ASIZE(NewName)-2); CurName=NewName; } if (CmpName(CmpMask,CurName,MatchMode)) return true; } } return false; }
static void GenArcName(wchar *ArcName,const wchar *GenerateMask,uint ArcNumber,bool &ArcNumPresent) { bool Prefix=false; if (*GenerateMask=='+') { Prefix=true; // Add the time string before the archive name. GenerateMask++; // Skip '+' in the beginning of time mask. } wchar Mask[MAX_GENERATE_MASK]; wcsncpyz(Mask,*GenerateMask!=0 ? GenerateMask:L"yyyymmddhhmmss",ASIZE(Mask)); bool QuoteMode=false,Hours=false; for (uint I=0;Mask[I]!=0;I++) { if (Mask[I]=='{' || Mask[I]=='}') { QuoteMode=(Mask[I]=='{'); continue; } if (QuoteMode) continue; int CurChar=toupperw(Mask[I]); if (CurChar=='H') Hours=true; if (Hours && CurChar=='M') { // Replace minutes with 'I'. We use 'M' both for months and minutes, // so we treat as minutes only those 'M' which are found after hours. Mask[I]='I'; } if (CurChar=='N') { uint Digits=GetDigits(ArcNumber); uint NCount=0; while (toupperw(Mask[I+NCount])=='N') NCount++; // Here we ensure that we have enough 'N' characters to fit all digits // of archive number. We'll replace them by actual number later // in this function. if (NCount<Digits) { wmemmove(Mask+I+Digits,Mask+I+NCount,wcslen(Mask+I+NCount)+1); wmemset(Mask+I,'N',Digits); } I+=Max(Digits,NCount)-1; ArcNumPresent=true; continue; } } RarTime CurTime; CurTime.SetCurrentTime(); RarLocalTime rlt; CurTime.GetLocal(&rlt); wchar Ext[NM],*Dot=GetExt(ArcName); *Ext=0; if (Dot==NULL) wcscpy(Ext,*PointToName(ArcName)==0 ? L".rar":L""); else { wcsncpyz(Ext,Dot,ASIZE(Ext)); *Dot=0; } int WeekDay=rlt.wDay==0 ? 6:rlt.wDay-1; int StartWeekDay=rlt.yDay-WeekDay; if (StartWeekDay<0) if (StartWeekDay<=-4) StartWeekDay+=IsLeapYear(rlt.Year-1) ? 366:365; else StartWeekDay=0; int CurWeek=StartWeekDay/7+1; if (StartWeekDay%7>=4) CurWeek++; char Field[10][6]; sprintf(Field[0],"%04d",rlt.Year); sprintf(Field[1],"%02d",rlt.Month); sprintf(Field[2],"%02d",rlt.Day); sprintf(Field[3],"%02d",rlt.Hour); sprintf(Field[4],"%02d",rlt.Minute); sprintf(Field[5],"%02d",rlt.Second); sprintf(Field[6],"%02d",CurWeek); sprintf(Field[7],"%d",WeekDay+1); sprintf(Field[8],"%03d",rlt.yDay+1); sprintf(Field[9],"%05d",ArcNumber); const wchar *MaskChars=L"YMDHISWAEN"; int CField[sizeof(Field)/sizeof(Field[0])]; memset(CField,0,sizeof(CField)); QuoteMode=false; for (int I=0;Mask[I]!=0;I++) { if (Mask[I]=='{' || Mask[I]=='}') { QuoteMode=(Mask[I]=='{'); continue; } if (QuoteMode) continue; const wchar *ChPtr=wcschr(MaskChars,toupperw(Mask[I])); if (ChPtr!=NULL) CField[ChPtr-MaskChars]++; } wchar DateText[MAX_GENERATE_MASK]; *DateText=0; QuoteMode=false; for (size_t I=0,J=0;Mask[I]!=0 && J<ASIZE(DateText)-1;I++) { if (Mask[I]=='{' || Mask[I]=='}') { QuoteMode=(Mask[I]=='{'); continue; } const wchar *ChPtr=wcschr(MaskChars,toupperw(Mask[I])); if (ChPtr==NULL || QuoteMode) { DateText[J]=Mask[I]; #ifdef _WIN_ALL // We do not allow ':' in Windows because of NTFS streams. // Users had problems after specifying hh:mm mask. if (DateText[J]==':') DateText[J]='_'; #endif } else { size_t FieldPos=ChPtr-MaskChars; int CharPos=(int)strlen(Field[FieldPos])-CField[FieldPos]--; if (FieldPos==1 && toupperw(Mask[I+1])=='M' && toupperw(Mask[I+2])=='M') { wcsncpyz(DateText+J,GetMonthName(rlt.Month-1),ASIZE(DateText)-J); J=wcslen(DateText); I+=2; continue; } if (CharPos<0) DateText[J]=Mask[I]; else DateText[J]=Field[FieldPos][CharPos]; } DateText[++J]=0; } if (Prefix) { wchar NewName[NM]; GetFilePath(ArcName,NewName,ASIZE(NewName)); AddEndSlash(NewName,ASIZE(NewName)); wcsncatz(NewName,DateText,ASIZE(NewName)); wcsncatz(NewName,PointToName(ArcName),ASIZE(NewName)); wcscpy(ArcName,NewName); } else wcscat(ArcName,DateText); wcscat(ArcName,Ext); }