| #include "rar.hpp" |
| |
| wchar* PointToName(const wchar *Path) |
| { |
| for (int I=(int)wcslen(Path)-1;I>=0;I--) |
| if (IsPathDiv(Path[I])) |
| return (wchar*)&Path[I+1]; |
| return (wchar*)((*Path && IsDriveDiv(Path[1])) ? Path+2:Path); |
| } |
| |
| |
| wchar* PointToLastChar(const wchar *Path) |
| { |
| size_t Length=wcslen(Path); |
| return (wchar*)(Length>0 ? Path+Length-1:Path); |
| } |
| |
| |
| wchar* ConvertPath(const wchar *SrcPath,wchar *DestPath) |
| { |
| const wchar *DestPtr=SrcPath; |
| |
| // Prevent \..\ in any part of path string. |
| for (const wchar *s=DestPtr;*s!=0;s++) |
| if (IsPathDiv(s[0]) && s[1]=='.' && s[2]=='.' && IsPathDiv(s[3])) |
| DestPtr=s+4; |
| |
| // Remove <d>:\ and any sequence of . and \ in the beginning of path string. |
| while (*DestPtr!=0) |
| { |
| const wchar *s=DestPtr; |
| if (s[0]!=0 && IsDriveDiv(s[1])) |
| s+=2; |
| if (s[0]=='\\' && s[1]=='\\') |
| { |
| const wchar *Slash=wcschr(s+2,'\\'); |
| if (Slash!=NULL && (Slash=wcschr(Slash+1,'\\'))!=NULL) |
| s=Slash+1; |
| } |
| for (const wchar *t=s;*t!=0;t++) |
| if (IsPathDiv(*t)) |
| s=t+1; |
| else |
| if (*t!='.') |
| break; |
| if (s==DestPtr) |
| break; |
| DestPtr=s; |
| } |
| |
| // Code above does not remove last "..", doing here. |
| if (DestPtr[0]=='.' && DestPtr[1]=='.' && DestPtr[2]==0) |
| DestPtr+=2; |
| |
| if (DestPath!=NULL) |
| { |
| // SrcPath and DestPath can point to same memory area, |
| // so we use the temporary buffer for copying. |
| wchar TmpStr[NM]; |
| wcsncpyz(TmpStr,DestPtr,ASIZE(TmpStr)); |
| wcscpy(DestPath,TmpStr); |
| } |
| return (wchar *)DestPtr; |
| } |
| |
| |
| void SetName(wchar *FullName,const wchar *Name,size_t MaxSize) |
| { |
| wchar *NamePtr=PointToName(FullName); |
| wcsncpyz(NamePtr,Name,MaxSize-(NamePtr-FullName)); |
| } |
| |
| |
| 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); |
| } |
| } |
| |
| |
| #ifndef SFX_MODULE |
| void SetSFXExt(wchar *SFXName,size_t MaxSize) |
| { |
| if (SFXName==NULL || *SFXName==0) |
| return; |
| |
| #ifdef _UNIX |
| SetExt(SFXName,L"sfx",MaxSize); |
| #endif |
| |
| #if defined(_WIN_ALL) || defined(_EMX) |
| SetExt(SFXName,L"exe",MaxSize); |
| #endif |
| } |
| #endif |
| |
| |
| // 'Ext' is an extension with the leading dot, like L".rar". |
| wchar *GetExt(const wchar *Name) |
| { |
| return Name==NULL ? NULL:wcsrchr(PointToName(Name),'.'); |
| } |
| |
| |
| // '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; |
| } |
| |
| |
| bool IsWildcard(const wchar *Str) |
| { |
| return Str==NULL ? false:wcspbrk(Str,L"*?")!=NULL; |
| } |
| |
| |
| bool IsPathDiv(int Ch) |
| { |
| #ifdef _WIN_ALL |
| return Ch=='\\' || Ch=='/'; |
| #else |
| return Ch==CPATHDIVIDER; |
| #endif |
| } |
| |
| |
| bool IsDriveDiv(int Ch) |
| { |
| #ifdef _UNIX |
| return false; |
| #else |
| return Ch==':'; |
| #endif |
| } |
| |
| |
| bool IsDriveLetter(const wchar *Path) |
| { |
| wchar Letter=etoupperw(Path[0]); |
| return Letter>='A' && Letter<='Z' && IsDriveDiv(Path[1]); |
| } |
| |
| |
| int GetPathDisk(const wchar *Path) |
| { |
| if (IsDriveLetter(Path)) |
| return etoupperw(*Path)-'A'; |
| else |
| return -1; |
| } |
| |
| |
| void AddEndSlash(wchar *Path,size_t MaxLength) |
| { |
| size_t Length=wcslen(Path); |
| if (Length>0 && Path[Length-1]!=CPATHDIVIDER && Length+1<MaxLength) |
| wcscat(Path,SPATHDIVIDER); |
| } |
| |
| |
| 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); |
| } |
| |
| |
| // Returns file path including the trailing path separator symbol. |
| void GetFilePath(const wchar *FullName,wchar *Path,size_t MaxLength) |
| { |
| if (MaxLength==0) |
| return; |
| size_t PathLength=Min(MaxLength-1,size_t(PointToName(FullName)-FullName)); |
| wcsncpy(Path,FullName,PathLength); |
| Path[PathLength]=0; |
| } |
| |
| |
| // Removes name and returns file path without the trailing |
| // path separator symbol. |
| void RemoveNameFromPath(wchar *Path) |
| { |
| wchar *Name=PointToName(Path); |
| if (Name>=Path+2 && (!IsDriveDiv(Path[1]) || Name>=Path+4)) |
| Name--; |
| *Name=0; |
| } |
| |
| |
| #if defined(_WIN_ALL) && !defined(SFX_MODULE) |
| 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; |
| } |
| #endif |
| |
| |
| #if defined(_WIN_ALL) && !defined(SFX_MODULE) |
| void GetRarDataPath(wchar *Path,size_t MaxSize,bool Create) |
| { |
| *Path=0; |
| |
| HKEY hKey; |
| if (RegOpenKeyEx(HKEY_CURRENT_USER,L"Software\\WinRAR\\Paths",0, |
| KEY_QUERY_VALUE,&hKey)==ERROR_SUCCESS) |
| { |
| DWORD DataSize=(DWORD)MaxSize,Type; |
| RegQueryValueEx(hKey,L"AppData",0,&Type,(BYTE *)Path,&DataSize); |
| RegCloseKey(hKey); |
| } |
| |
| if (*Path==0 || !FileExist(Path)) |
| if (!GetAppDataPath(Path,MaxSize,Create)) |
| { |
| GetModuleFileName(NULL,Path,(DWORD)MaxSize); |
| RemoveNameFromPath(Path); |
| } |
| } |
| #endif |
| |
| |
| #ifndef SFX_MODULE |
| bool EnumConfigPaths(uint Number,wchar *Path,size_t MaxSize,bool Create) |
| { |
| #ifdef _UNIX |
| static const wchar *ConfPath[]={ |
| L"/etc", L"/etc/rar", L"/usr/lib", L"/usr/local/lib", L"/usr/local/etc" |
| }; |
| if (Number==0) |
| { |
| char *EnvStr=getenv("HOME"); |
| if (EnvStr!=NULL) |
| CharToWide(EnvStr,Path,MaxSize); |
| else |
| wcsncpyz(Path,ConfPath[0],MaxSize); |
| return true; |
| } |
| Number--; |
| if (Number>=ASIZE(ConfPath)) |
| return false; |
| wcsncpyz(Path,ConfPath[Number], MaxSize); |
| return true; |
| #elif defined(_WIN_ALL) |
| if (Number>1) |
| return false; |
| if (Number==0) |
| GetRarDataPath(Path,MaxSize,Create); |
| else |
| { |
| GetModuleFileName(NULL,Path,(DWORD)MaxSize); |
| RemoveNameFromPath(Path); |
| } |
| return true; |
| #else |
| return false; |
| #endif |
| } |
| #endif |
| |
| |
| #ifndef SFX_MODULE |
| 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; |
| } |
| } |
| #endif |
| |
| |
| // Returns a pointer to rightmost digit of volume number. |
| wchar* GetVolNumPart(const wchar *ArcName) |
| { |
| // Pointing to last name character. |
| const wchar *ChPtr=ArcName+wcslen(ArcName)-1; |
| |
| // Skipping the archive extension. |
| while (!IsDigit(*ChPtr) && ChPtr>ArcName) |
| ChPtr--; |
| |
| // Skipping the numeric part of name. |
| const wchar *NumPtr=ChPtr; |
| while (IsDigit(*NumPtr) && NumPtr>ArcName) |
| NumPtr--; |
| |
| // Searching for first numeric part in names like name.part##of##.rar. |
| // Stop search on the first dot. |
| while (NumPtr>ArcName && *NumPtr!='.') |
| { |
| if (IsDigit(*NumPtr)) |
| { |
| // Validate the first numeric part only if it has a dot somewhere |
| // before it. |
| wchar *Dot=wcschr(PointToName(ArcName),'.'); |
| if (Dot!=NULL && Dot<NumPtr) |
| ChPtr=NumPtr; |
| break; |
| } |
| NumPtr--; |
| } |
| return (wchar *)ChPtr; |
| } |
| |
| |
| 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--; |
| } |
| } |
| } |
| |
| |
| bool IsNameUsable(const wchar *Name) |
| { |
| #ifndef _UNIX |
| if (Name[0] && Name[1] && wcschr(Name+2,':')!=NULL) |
| return false; |
| for (const wchar *s=Name;*s!=0;s++) |
| { |
| if ((uint)*s<32) |
| return false; |
| if ((*s==' ' || *s=='.') && IsPathDiv(s[1])) |
| return false; |
| } |
| #endif |
| return *Name!=0 && wcspbrk(Name,L"?*<>|\"")==NULL; |
| } |
| |
| |
| void MakeNameUsable(char *Name,bool Extended) |
| { |
| #ifdef _WIN_ALL |
| // In Windows we also need to convert characters not defined in current |
| // code page. This double conversion changes them to '?', which is |
| // catched by code below. |
| size_t NameLength=strlen(Name); |
| wchar NameW[NM]; |
| CharToWide(Name,NameW,ASIZE(NameW)); |
| WideToChar(NameW,Name,NameLength+1); |
| Name[NameLength]=0; |
| #endif |
| for (char *s=Name;*s!=0;s=charnext(s)) |
| { |
| if (strchr(Extended ? "?*<>|\"":"?*",*s)!=NULL || (Extended && (byte)*s<32)) |
| *s='_'; |
| #ifdef _EMX |
| if (*s=='=') |
| *s='_'; |
| #endif |
| #ifndef _UNIX |
| if (s-Name>1 && *s==':') |
| *s='_'; |
| // Remove ' ' and '.' before path separator, but allow .\ and ..\. |
| if ((*s==' ' || (*s=='.' && s>Name && !IsPathDiv(s[-1]) && s[-1]!='.')) && IsPathDiv(s[1])) |
| *s='_'; |
| #endif |
| } |
| } |
| |
| |
| void MakeNameUsable(wchar *Name,bool Extended) |
| { |
| for (wchar *s=Name;*s!=0;s++) |
| { |
| if (wcschr(Extended ? L"?*<>|\"":L"?*",*s)!=NULL || (Extended && (uint)*s<32)) |
| *s='_'; |
| #ifndef _UNIX |
| if (s-Name>1 && *s==':') |
| *s='_'; |
| #if 0 // We already can create such files. |
| // Remove ' ' and '.' before path separator, but allow .\ and ..\. |
| if (IsPathDiv(s[1]) && (*s==' ' || *s=='.' && s>Name && |
| !IsPathDiv(s[-1]) && (s[-1]!='.' || s>Name+1 && !IsPathDiv(s[-2])))) |
| *s='_'; |
| #endif |
| #endif |
| } |
| } |
| |
| |
| void UnixSlashToDos(const char *SrcName,char *DestName,size_t MaxLength) |
| { |
| size_t Copied=0; |
| for (;Copied<MaxLength-1 && SrcName[Copied]!=0;Copied++) |
| DestName[Copied]=SrcName[Copied]=='/' ? '\\':SrcName[Copied]; |
| DestName[Copied]=0; |
| } |
| |
| |
| void DosSlashToUnix(const char *SrcName,char *DestName,size_t MaxLength) |
| { |
| size_t Copied=0; |
| for (;Copied<MaxLength-1 && SrcName[Copied]!=0;Copied++) |
| DestName[Copied]=SrcName[Copied]=='\\' ? '/':SrcName[Copied]; |
| DestName[Copied]=0; |
| } |
| |
| |
| void UnixSlashToDos(const wchar *SrcName,wchar *DestName,size_t MaxLength) |
| { |
| size_t Copied=0; |
| for (;Copied<MaxLength-1 && SrcName[Copied]!=0;Copied++) |
| DestName[Copied]=SrcName[Copied]=='/' ? '\\':SrcName[Copied]; |
| DestName[Copied]=0; |
| } |
| |
| |
| void DosSlashToUnix(const wchar *SrcName,wchar *DestName,size_t MaxLength) |
| { |
| size_t Copied=0; |
| for (;Copied<MaxLength-1 && SrcName[Copied]!=0;Copied++) |
| DestName[Copied]=SrcName[Copied]=='\\' ? '/':SrcName[Copied]; |
| DestName[Copied]=0; |
| } |
| |
| |
| 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 |
| } |
| |
| |
| bool IsFullPath(const wchar *Path) |
| { |
| /* |
| wchar PathOnly[NM]; |
| GetFilePath(Path,PathOnly,ASIZE(PathOnly)); |
| if (IsWildcard(PathOnly)) |
| return true; |
| */ |
| #if defined(_WIN_ALL) || defined(_EMX) |
| return (Path[0]=='\\' && Path[1]=='\\') || (IsDriveLetter(Path) && IsPathDiv(Path[2])); |
| #else |
| return IsPathDiv(Path[0]); |
| #endif |
| } |
| |
| |
| bool IsFullRootPath(const wchar *Path) |
| { |
| return IsFullPath(Path) || IsPathDiv(Path[0]); |
| } |
| |
| |
| void GetPathRoot(const wchar *Path,wchar *Root,size_t MaxSize) |
| { |
| *Root=0; |
| if (IsDriveLetter(Path)) |
| swprintf(Root,MaxSize,L"%c:\\",*Path); |
| else |
| if (Path[0]=='\\' && Path[1]=='\\') |
| { |
| const wchar *Slash=wcschr(Path+2,'\\'); |
| if (Slash!=NULL) |
| { |
| size_t Length; |
| if ((Slash=wcschr(Slash+1,'\\'))!=NULL) |
| Length=Slash-Path+1; |
| else |
| Length=wcslen(Path); |
| if (Length>=MaxSize) |
| Length=0; |
| wcsncpy(Root,Path,Length); |
| Root[Length]=0; |
| } |
| } |
| } |
| |
| |
| int ParseVersionFileName(wchar *Name,bool Truncate) |
| { |
| int Version=0; |
| wchar *VerText=wcsrchr(Name,';'); |
| if (VerText!=NULL) |
| { |
| if (Version==0) |
| Version=atoiw(VerText+1); |
| if (Truncate) |
| *VerText=0; |
| } |
| return Version; |
| } |
| |
| |
| #if !defined(SFX_MODULE) |
| // Get the name of first volume. Return the leftmost digit of volume number. |
| wchar* VolNameToFirstName(const wchar *VolName,wchar *FirstName,size_t MaxSize,bool NewNumbering) |
| { |
| if (FirstName!=VolName) |
| wcsncpyz(FirstName,VolName,MaxSize); |
| wchar *VolNumStart=FirstName; |
| if (NewNumbering) |
| { |
| wchar N='1'; |
| |
| // From the rightmost digit of volume number to the left. |
| for (wchar *ChPtr=GetVolNumPart(FirstName);ChPtr>FirstName;ChPtr--) |
| if (IsDigit(*ChPtr)) |
| { |
| *ChPtr=N; // Set the rightmost digit to '1' and others to '0'. |
| N='0'; |
| } |
| else |
| if (N=='0') |
| { |
| VolNumStart=ChPtr+1; // Store the position of leftmost digit in volume number. |
| break; |
| } |
| } |
| else |
| { |
| // Old volume numbering scheme. Just set the extension to ".rar". |
| SetExt(FirstName,L"rar",MaxSize); |
| VolNumStart=GetExt(FirstName); |
| } |
| if (!FileExist(FirstName)) |
| { |
| // If the first volume, which name we just generated, is not exist, |
| // check if volume with same name and any other extension is available. |
| // It can help in case of *.exe or *.sfx first volume. |
| wchar Mask[NM]; |
| wcsncpyz(Mask,FirstName,ASIZE(Mask)); |
| SetExt(Mask,L"*",ASIZE(Mask)); |
| FindFile Find; |
| Find.SetMask(Mask); |
| FindData FD; |
| while (Find.Next(&FD)) |
| { |
| Archive Arc; |
| if (Arc.Open(FD.Name,0) && Arc.IsArchive(true) && Arc.FirstVolume) |
| { |
| wcsncpyz(FirstName,FD.Name,MaxSize); |
| break; |
| } |
| } |
| } |
| return VolNumStart; |
| } |
| #endif |
| |
| |
| #ifndef SFX_MODULE |
| 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],"%04u",rlt.Year); |
| sprintf(Field[1],"%02u",rlt.Month); |
| sprintf(Field[2],"%02u",rlt.Day); |
| sprintf(Field[3],"%02u",rlt.Hour); |
| sprintf(Field[4],"%02u",rlt.Minute); |
| sprintf(Field[5],"%02u",rlt.Second); |
| sprintf(Field[6],"%02u",(uint)CurWeek); |
| sprintf(Field[7],"%u",(uint)WeekDay+1); |
| sprintf(Field[8],"%03u",rlt.yDay+1); |
| sprintf(Field[9],"%05u",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); |
| } |
| |
| |
| void GenerateArchiveName(wchar *ArcName,size_t MaxSize,const wchar *GenerateMask,bool Archiving) |
| { |
| // Must be enough space for archive name plus all stuff in mask plus |
| // extra overhead produced by mask 'N' (archive number) characters. |
| // One 'N' character can result in several numbers if we process more |
| // than 9 archives. |
| wchar NewName[NM+MAX_GENERATE_MASK+20]; |
| |
| uint ArcNumber=1; |
| while (true) // Loop for 'N' (archive number) processing. |
| { |
| wcsncpyz(NewName,ArcName,ASIZE(NewName)); |
| |
| bool ArcNumPresent=false; |
| |
| GenArcName(NewName,GenerateMask,ArcNumber,ArcNumPresent); |
| |
| if (!ArcNumPresent) |
| break; |
| if (!FileExist(NewName)) |
| { |
| if (!Archiving && ArcNumber>1) |
| { |
| // If we perform non-archiving operation, we need to use the last |
| // existing archive before the first unused name. So we generate |
| // the name for (ArcNumber-1) below. |
| wcsncpyz(NewName,NullToEmpty(ArcName),ASIZE(NewName)); |
| GenArcName(NewName,GenerateMask,ArcNumber-1,ArcNumPresent); |
| } |
| break; |
| } |
| ArcNumber++; |
| } |
| wcsncpyz(ArcName,NewName,MaxSize); |
| } |
| #endif |
| |
| |
| wchar* GetWideName(const char *Name,const wchar *NameW,wchar *DestW,size_t DestSize) |
| { |
| if (NameW!=NULL && *NameW!=0) |
| { |
| if (DestW!=NameW) |
| wcsncpy(DestW,NameW,DestSize); |
| } |
| else |
| if (Name!=NULL) |
| CharToWide(Name,DestW,DestSize); |
| else |
| *DestW=0; |
| |
| // Ensure that we return a zero terminate string for security reasons. |
| if (DestSize>0) |
| DestW[DestSize-1]=0; |
| |
| return DestW; |
| } |
| |
| |
| #ifdef _WIN_ALL |
| // 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=IsDriveLetter(Src) && IsPathDiv(Src[2]); |
| size_t SrcLength=wcslen(Src); |
| if (IsFullPath(Src)) // Paths in d:\path\name format. |
| { |
| if (IsDriveLetter(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; |
| } |
| |
| |
| // Convert Unix, OS X and Android decomposed chracters to Windows precomposed. |
| void ConvertToPrecomposed(wchar *Name,size_t NameSize) |
| { |
| wchar FileName[NM]; |
| if (WinNT()>=WNT_VISTA && // MAP_PRECOMPOSED is not supported in XP. |
| FoldString(MAP_PRECOMPOSED,Name,-1,FileName,ASIZE(FileName))!=0) |
| { |
| FileName[ASIZE(FileName)-1]=0; |
| wcsncpyz(Name,FileName,NameSize); |
| } |
| } |
| |
| |
| // Remove trailing spaces and dots in file name and in dir names in path. |
| void MakeNameCompatible(wchar *Name) |
| { |
| int Src=0,Dest=0; |
| while (true) |
| { |
| if (IsPathDiv(Name[Src]) || Name[Src]==0) |
| for (int I=Dest-1;I>0 && (Name[I]==' ' || Name[I]=='.');I--) |
| { |
| // Permit path1/./path2 and ../path1 paths. |
| if (Name[I]=='.' && (IsPathDiv(Name[I-1]) || (Name[I-1]=='.' && I==1))) |
| break; |
| Dest--; |
| } |
| Name[Dest]=Name[Src]; |
| if (Name[Src]==0) |
| break; |
| Src++; |
| Dest++; |
| } |
| } |
| #endif |