#include "rar.hpp" char* PointToName(const char *Path) { const char *Found=NULL; for (const char *s=Path;*s!=0;s=charnext(s)) if (IsPathDiv(*s)) Found=(char*)(s+1); if (Found!=NULL) return((char*)Found); return (char*)((*Path && IsDriveDiv(Path[1]) && charnext(Path)==Path+1) ? Path+2:Path); } 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); } char* PointToLastChar(const char *Path) { for (const char *s=Path,*p=Path;;p=s,s=charnext(s)) if (*s==0) return((char *)p); } wchar* PointToLastChar(const wchar *Path) { size_t Length=wcslen(Path); return((wchar*)(Length>0 ? Path+Length-1:Path)); } char* ConvertPath(const char *SrcPath,char *DestPath) { const char *DestPtr=SrcPath; // Prevent \..\ in any part of path string. for (const char *s=DestPtr;*s!=0;s++) if (IsPathDiv(s[0]) && s[1]=='.' && s[2]=='.' && IsPathDiv(s[3])) DestPtr=s+4; // Remove :\ and any sequence of . and \ in the beginning of path string. while (*DestPtr!=0) { const char *s=DestPtr; if (s[0] && IsDriveDiv(s[1])) s+=2; else if (s[0]=='\\' && s[1]=='\\') { const char *Slash=strchr(s+2,'\\'); if (Slash!=NULL && (Slash=strchr(Slash+1,'\\'))!=NULL) s=Slash+1; } for (const char *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. char TmpStr[NM]; strncpyz(TmpStr,DestPtr,ASIZE(TmpStr)); strcpy(DestPath,TmpStr); } return((char *)DestPtr); } 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 :\ and any sequence of . and \ in the beginning of path string. while (*DestPtr!=0) { const wchar *s=DestPtr; if (s[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 SetExt(char *Name,const char *NewExt) { char *Dot=GetExt(Name); if (NewExt==NULL) { if (Dot!=NULL) *Dot=0; } else if (Dot==NULL) { strcat(Name,"."); strcat(Name,NewExt); } else strcpy(Dot+1,NewExt); } void SetExt(wchar *Name,const wchar *NewExt) { if (Name==NULL || *Name==0) return; wchar *Dot=GetExt(Name); if (NewExt==NULL) { if (Dot!=NULL) *Dot=0; } else if (Dot==NULL) { wcscat(Name,L"."); wcscat(Name,NewExt); } else wcscpy(Dot+1,NewExt); } #ifndef SFX_MODULE void SetSFXExt(char *SFXName) { #ifdef _UNIX SetExt(SFXName,"sfx"); #endif #if defined(_WIN_ALL) || defined(_EMX) SetExt(SFXName,"exe"); #endif } #endif #ifndef SFX_MODULE void SetSFXExt(wchar *SFXName) { if (SFXName==NULL || *SFXName==0) return; #ifdef _UNIX SetExt(SFXName,L"sfx"); #endif #if defined(_WIN_ALL) || defined(_EMX) SetExt(SFXName,L"exe"); #endif } #endif char *GetExt(const char *Name) { return(Name==NULL ? NULL:strrchrd(PointToName(Name),'.')); } wchar *GetExt(const wchar *Name) { return(Name==NULL ? NULL:wcsrchr(PointToName(Name),'.')); } // 'Ext' is an extension without the leading dot, like "rar". bool CmpExt(const char *Name,const char *Ext) { char *NameExt=GetExt(Name); return(NameExt!=NULL && stricomp(NameExt+1,Ext)==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); } bool IsWildcard(const char *Str,const wchar *StrW) { if (StrW!=NULL && *StrW!=0) return(wcspbrk(StrW,L"*?")!=NULL); return(Str==NULL ? false:strpbrk(Str,"*?")!=NULL); } bool IsPathDiv(int Ch) { #if defined(_WIN_ALL) || defined(_EMX) return(Ch=='\\' || Ch=='/'); #else return(Ch==CPATHDIVIDER); #endif } bool IsDriveDiv(int Ch) { #ifdef _UNIX return(false); #else return(Ch==':'); #endif } int GetPathDisk(const char *Path) { if (IsDiskLetter(Path)) return(etoupper(*Path)-'A'); else return(-1); } int GetPathDisk(const wchar *Path) { if (IsDiskLetter(Path)) return(etoupperw(*Path)-'A'); else return(-1); } void AddEndSlash(char *Path) { char *LastChar=PointToLastChar(Path); if (*LastChar!=0 && *LastChar!=CPATHDIVIDER) strcat(LastChar,PATHDIVIDER); } void AddEndSlash(wchar *Path) { size_t Length=wcslen(Path); if (Length>0 && Path[Length-1]!=CPATHDIVIDER) wcscat(Path,PATHDIVIDERW); } // Returns file path including the trailing path separator symbol. void GetFilePath(const char *FullName,char *Path,int MaxLength) { size_t PathLength=Min(MaxLength-1,PointToName(FullName)-FullName); strncpy(Path,FullName,PathLength); Path[PathLength]=0; } // Returns file path including the trailing path separator symbol. void GetFilePath(const wchar *FullName,wchar *Path,int MaxLength) { size_t PathLength=Min(MaxLength-1,PointToName(FullName)-FullName); wcsncpy(Path,FullName,PathLength); Path[PathLength]=0; } // Removes name and returns file path without the trailing // path separator symbol. void RemoveNameFromPath(char *Path) { char *Name=PointToName(Path); if (Name>=Path+2 && (!IsDriveDiv(Path[1]) || Name>=Path+4)) Name--; *Name=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(_WIN_CE) && !defined(SFX_MODULE) void GetAppDataPath(char *Path) { LPMALLOC g_pMalloc; SHGetMalloc(&g_pMalloc); LPITEMIDLIST ppidl; *Path=0; bool Success=false; if (SHGetSpecialFolderLocation(NULL,CSIDL_APPDATA,&ppidl)==NOERROR && SHGetPathFromIDListA(ppidl,Path) && *Path!=0) { AddEndSlash(Path); strcat(Path,"WinRAR"); Success=FileExist(Path) || MakeDir(Path,NULL,false,0)==MKDIR_SUCCESS; } if (!Success) { GetModuleFileNameA(NULL,Path,NM); RemoveNameFromPath(Path); } g_pMalloc->Free(ppidl); } #endif #if defined(_WIN_ALL) && !defined(_WIN_CE) && !defined(SFX_MODULE) void GetAppDataPath(wchar *Path) { LPMALLOC g_pMalloc; SHGetMalloc(&g_pMalloc); LPITEMIDLIST ppidl; *Path=0; bool Success=false; if (SHGetSpecialFolderLocation(NULL,CSIDL_APPDATA,&ppidl)==NOERROR && SHGetPathFromIDListW(ppidl,Path) && *Path!=0) { AddEndSlash(Path); wcscat(Path,L"WinRAR"); Success=FileExist(NULL,Path) || MakeDir(NULL,Path,false,0)==MKDIR_SUCCESS; } if (!Success) { GetModuleFileNameW(NULL,Path,NM); RemoveNameFromPath(Path); } g_pMalloc->Free(ppidl); } #endif #if defined(_WIN_ALL) && !defined(_WIN_CE) && !defined(SFX_MODULE) void GetRarDataPath(char *Path) { *Path=0; HKEY hKey; if (RegOpenKeyExA(HKEY_CURRENT_USER,"Software\\WinRAR\\Paths",0, KEY_QUERY_VALUE,&hKey)==ERROR_SUCCESS) { DWORD DataSize=NM,Type; RegQueryValueExA(hKey,"AppData",0,&Type,(BYTE *)Path,&DataSize); RegCloseKey(hKey); } if (*Path==0 || !FileExist(Path)) GetAppDataPath(Path); } #endif #if defined(_WIN_ALL) && !defined(_WIN_CE) && !defined(SFX_MODULE) void GetRarDataPath(wchar *Path) { *Path=0; HKEY hKey; if (RegOpenKeyExW(HKEY_CURRENT_USER,L"Software\\WinRAR\\Paths",0, KEY_QUERY_VALUE,&hKey)==ERROR_SUCCESS) { DWORD DataSize=NM,Type; RegQueryValueExW(hKey,L"AppData",0,&Type,(BYTE *)Path,&DataSize); RegCloseKey(hKey); } if (*Path==0 || !FileExist(NULL,Path)) GetAppDataPath(Path); } #endif #ifndef SFX_MODULE bool EnumConfigPaths(char *Path,int Number) { #ifdef _EMX static char RARFileName[NM]; if (Number==-1) strcpy(RARFileName,Path); if (Number!=0) return(false); #ifndef _DJGPP if (_osmode==OS2_MODE) { PTIB ptib; PPIB ppib; DosGetInfoBlocks(&ptib, &ppib); DosQueryModuleName(ppib->pib_hmte,NM,Path); } else #endif strcpy(Path,RARFileName); RemoveNameFromPath(Path); return(true); #elif defined(_UNIX) static const char *AltPath[]={ "/etc","/etc/rar","/usr/lib","/usr/local/lib","/usr/local/etc" }; if (Number==0) { char *EnvStr=getenv("HOME"); strncpy(Path, (EnvStr==NULL) ? AltPath[0] : EnvStr, NM-1); Path[NM-1]=0; return(true); } Number--; if (Number<0 || Number>=sizeof(AltPath)/sizeof(AltPath[0])) return(false); strcpy(Path,AltPath[Number]); return(true); #elif defined(_WIN_ALL) if (Number<0 || Number>1) return(false); if (Number==0) GetRarDataPath(Path); else { GetModuleFileNameA(NULL,Path,NM); RemoveNameFromPath(Path); } return(true); #else return(false); #endif } #endif #if defined(_WIN_ALL) && !defined(SFX_MODULE) bool EnumConfigPaths(wchar *Path,int Number) { if (Number<0 || Number>1) return(false); if (Number==0) GetRarDataPath(Path); else { GetModuleFileNameW(NULL,Path,NM); RemoveNameFromPath(Path); } return(true); } #endif #ifndef SFX_MODULE void GetConfigName(const char *Name,char *FullName,bool CheckExist) { *FullName=0; for (int I=0;EnumConfigPaths(FullName,I);I++) { AddEndSlash(FullName); strcat(FullName,Name); if (!CheckExist || WildFileExist(FullName)) break; } } #endif #if defined(_WIN_ALL) && !defined(SFX_MODULE) void GetConfigName(const wchar *Name,wchar *FullName,bool CheckExist) { *FullName=0; for (int I=0;EnumConfigPaths(FullName,I);I++) { AddEndSlash(FullName); wcscat(FullName,Name); if (!CheckExist || WildFileExist(NULL,FullName)) break; } } #endif // Returns a pointer to rightmost digit of volume number. char* GetVolNumPart(char *ArcName) { // Pointing to last name character. char *ChPtr=ArcName+strlen(ArcName)-1; // Skipping the archive extension. while (!IsDigit(*ChPtr) && ChPtr>ArcName) ChPtr--; // Skipping the numeric part of name. char *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. char *Dot=strchrd(PointToName(ArcName),'.'); if (Dot!=NULL && DotArcName) ChPtr--; // Skipping the numeric part of name. 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|\"")==NULL); } 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==' ' && 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='_'; if (*s==' ' && 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 (*s==' ' && IsPathDiv(s[1])) *s='_'; #endif } } char* UnixSlashToDos(char *SrcName,char *DestName,uint MaxLength) { if (DestName!=NULL && DestName!=SrcName) if (strlen(SrcName)>=MaxLength) { *DestName=0; return(DestName); } else strcpy(DestName,SrcName); for (char *s=SrcName;*s!=0;s=charnext(s)) { if (*s=='/') if (DestName==NULL) *s='\\'; else DestName[s-SrcName]='\\'; } return(DestName==NULL ? SrcName:DestName); } char* DosSlashToUnix(char *SrcName,char *DestName,uint MaxLength) { if (DestName!=NULL && DestName!=SrcName) if (strlen(SrcName)>=MaxLength) { *DestName=0; return(DestName); } else strcpy(DestName,SrcName); for (char *s=SrcName;*s!=0;s=charnext(s)) { if (*s=='\\') if (DestName==NULL) *s='/'; else DestName[s-SrcName]='/'; } return(DestName==NULL ? SrcName:DestName); } wchar* UnixSlashToDos(wchar *SrcName,wchar *DestName,uint MaxLength) { if (DestName!=NULL && DestName!=SrcName) if (wcslen(SrcName)>=MaxLength) { *DestName=0; return(DestName); } else wcscpy(DestName,SrcName); for (wchar *s=SrcName;*s!=0;s++) { if (*s=='/') if (DestName==NULL) *s='\\'; else DestName[s-SrcName]='\\'; } return(DestName==NULL ? SrcName:DestName); } wchar* DosSlashToUnix(wchar *SrcName,wchar *DestName,uint MaxLength) { if (DestName!=NULL && DestName!=SrcName) if (wcslen(SrcName)>=MaxLength) { *DestName=0; return(DestName); } else wcscpy(DestName,SrcName); for (wchar *s=SrcName;*s!=0;s++) { if (*s=='\\') if (DestName==NULL) *s='/'; else DestName[s-SrcName]='/'; } return(DestName==NULL ? SrcName:DestName); } void ConvertNameToFull(const char *Src,char *Dest) { #ifdef _WIN_ALL #ifndef _WIN_CE char FullName[NM],*NamePtr; DWORD Code=GetFullPathNameA(Src,ASIZE(FullName),FullName,&NamePtr); if (Code!=0 && Code='A' && Letter<='Z' && IsDriveDiv(Path[1])); } bool IsDiskLetter(const wchar *Path) { wchar Letter=etoupperw(Path[0]); return(Letter>='A' && Letter<='Z' && IsDriveDiv(Path[1])); } void GetPathRoot(const char *Path,char *Root) { *Root=0; if (IsDiskLetter(Path)) sprintf(Root,"%c:\\",*Path); else if (Path[0]=='\\' && Path[1]=='\\') { const char *Slash=strchr(Path+2,'\\'); if (Slash!=NULL) { size_t Length; if ((Slash=strchr(Slash+1,'\\'))!=NULL) Length=Slash-Path+1; else Length=strlen(Path); strncpy(Root,Path,Length); Root[Length]=0; } } } void GetPathRoot(const wchar *Path,wchar *Root) { *Root=0; if (IsDiskLetter(Path)) sprintfw(Root,4,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); wcsncpy(Root,Path,Length); Root[Length]=0; } } } int ParseVersionFileName(char *Name,wchar *NameW,bool Truncate) { int Version=0; char *VerText=strrchrd(Name,';'); if (VerText!=NULL) { Version=atoi(VerText+1); if (Truncate) *VerText=0; } if (NameW!=NULL) { wchar *VerTextW=wcsrchr(NameW,';'); if (VerTextW!=NULL) { if (Version==0) Version=atoiw(VerTextW+1); if (Truncate) *VerTextW=0; } } return(Version); } #if !defined(SFX_MODULE) && !defined(SETUP) // Get the name of first volume. Return the leftmost digit of volume number. char* VolNameToFirstName(const char *VolName,char *FirstName,bool NewNumbering) { if (FirstName!=VolName) strcpy(FirstName,VolName); char *VolNumStart=FirstName; if (NewNumbering) { char N='1'; // From the rightmost digit of volume number to the left. for (char *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,"rar"); 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. char Mask[NM]; strcpy(Mask,FirstName); SetExt(Mask,"*"); FindFile Find; Find.SetMask(Mask); FindData FD; while (Find.Next(&FD)) { Archive Arc; if (Arc.Open(FD.Name,FD.NameW,0) && Arc.IsArchive(true) && !Arc.NotFirstVolume) { strcpy(FirstName,FD.Name); break; } } } return(VolNumStart); } #endif #if !defined(SFX_MODULE) && !defined(SETUP) // Get the name of first volume. Return the leftmost digit of volume number. wchar* VolNameToFirstName(const wchar *VolName,wchar *FirstName,bool NewNumbering) { if (FirstName!=VolName) wcscpy(FirstName,VolName); 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"); VolNumStart=GetExt(FirstName); } if (!FileExist(NULL,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]; wcscpy(Mask,FirstName); SetExt(Mask,L"*"); FindFile Find; Find.SetMaskW(Mask); FindData FD; while (Find.Next(&FD)) { Archive Arc; if (Arc.Open(FD.Name,FD.NameW,0) && Arc.IsArchive(true) && !Arc.NotFirstVolume) { wcscpy(FirstName,FD.NameW); break; } } } return(VolNumStart); } #endif #ifndef SFX_MODULE static void GenArcName(char *ArcName,wchar *ArcNameW,char *GenerateMask, uint ArcNumber,bool &ArcNumPresent); void GenerateArchiveName(char *ArcName,wchar *ArcNameW,size_t MaxSize, char *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. char NewName[NM+MAX_GENERATE_MASK+20]; wchar NewNameW[NM+MAX_GENERATE_MASK+20]; uint ArcNumber=1; while (true) // Loop for 'N' (archive number) processing. { strncpyz(NewName,NullToEmpty(ArcName),ASIZE(NewName)); wcsncpyz(NewNameW,NullToEmpty(ArcNameW),ASIZE(NewNameW)); bool ArcNumPresent=false; GenArcName(NewName,NewNameW,GenerateMask,ArcNumber,ArcNumPresent); if (!ArcNumPresent) break; if (!FileExist(NewName,NewNameW)) { 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. strncpyz(NewName,NullToEmpty(ArcName),ASIZE(NewName)); wcsncpyz(NewNameW,NullToEmpty(ArcNameW),ASIZE(NewNameW)); GenArcName(NewName,NewNameW,GenerateMask,ArcNumber-1,ArcNumPresent); } break; } ArcNumber++; } if (ArcName!=NULL && *ArcName!=0) strncpyz(ArcName,NewName,MaxSize); if (ArcNameW!=NULL && *ArcNameW!=0) wcsncpyz(ArcNameW,NewNameW,MaxSize); } void GenArcName(char *ArcName,wchar *ArcNameW,char *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. } char Mask[MAX_GENERATE_MASK]; strncpyz(Mask,*GenerateMask ? GenerateMask:"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=etoupper(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 (etoupper(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=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 char *MaskChars="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 char *Ch=strchr(MaskChars,etoupper(Mask[I])); if (Ch!=NULL) CField[Ch-MaskChars]++; } char DateText[MAX_GENERATE_MASK]; *DateText=0; QuoteMode=false; for (size_t I=0,J=0;Mask[I]!=0 && J0) DestW[DestSize-1]=0; return(DestW); } // Unlike WideToChar, always returns the zero terminated string, // even if the destination buffer size is not enough. char* GetAsciiName(const wchar *NameW,char *Name,size_t DestSize) { if (DestSize>0) { WideToChar(NameW,Name,DestSize); Name[DestSize-1]=0; } else *Name=0; return Name; }