1 #include "rar.hpp"
2 
PointToName(const wchar * Path)3 wchar* PointToName(const wchar *Path)
4 {
5   for (int I=(int)wcslen(Path)-1;I>=0;I--)
6     if (IsPathDiv(Path[I]))
7       return (wchar*)&Path[I+1];
8   return (wchar*)((*Path && IsDriveDiv(Path[1])) ? Path+2:Path);
9 }
10 
11 
PointToLastChar(const wchar * Path)12 wchar* PointToLastChar(const wchar *Path)
13 {
14   size_t Length=wcslen(Path);
15   return (wchar*)(Length>0 ? Path+Length-1:Path);
16 }
17 
18 
ConvertPath(const wchar * SrcPath,wchar * DestPath,size_t DestSize)19 wchar* ConvertPath(const wchar *SrcPath,wchar *DestPath,size_t DestSize)
20 {
21   const wchar *DestPtr=SrcPath;
22 
23   // Prevent \..\ in any part of path string.
24   for (const wchar *s=DestPtr;*s!=0;s++)
25     if (IsPathDiv(s[0]) && s[1]=='.' && s[2]=='.' && IsPathDiv(s[3]))
26       DestPtr=s+4;
27 
28   // Remove any amount of <d>:\ and any sequence of . and \ in the beginning of path string.
29   while (*DestPtr!=0)
30   {
31     const wchar *s=DestPtr;
32     if (s[0]!=0 && IsDriveDiv(s[1]))
33       s+=2;
34     if (s[0]=='\\' && s[1]=='\\')
35     {
36       const wchar *Slash=wcschr(s+2,'\\');
37       if (Slash!=NULL && (Slash=wcschr(Slash+1,'\\'))!=NULL)
38         s=Slash+1;
39     }
40     for (const wchar *t=s;*t!=0;t++)
41       if (IsPathDiv(*t))
42         s=t+1;
43       else
44         if (*t!='.')
45           break;
46     if (s==DestPtr)
47       break;
48     DestPtr=s;
49   }
50 
51   // Code above does not remove last "..", doing here.
52   if (DestPtr[0]=='.' && DestPtr[1]=='.' && DestPtr[2]==0)
53     DestPtr+=2;
54 
55   if (DestPath!=NULL)
56   {
57     // SrcPath and DestPath can point to same memory area,
58     // so we use the temporary buffer for copying.
59     wchar TmpStr[NM];
60     wcsncpyz(TmpStr,DestPtr,ASIZE(TmpStr));
61     wcsncpyz(DestPath,TmpStr,DestSize);
62   }
63   return (wchar *)DestPtr;
64 }
65 
66 
SetName(wchar * FullName,const wchar * Name,size_t MaxSize)67 void SetName(wchar *FullName,const wchar *Name,size_t MaxSize)
68 {
69   wchar *NamePtr=PointToName(FullName);
70   wcsncpyz(NamePtr,Name,MaxSize-(NamePtr-FullName));
71 }
72 
73 
SetExt(wchar * Name,const wchar * NewExt,size_t MaxSize)74 void SetExt(wchar *Name,const wchar *NewExt,size_t MaxSize)
75 {
76   if (Name==NULL || *Name==0)
77     return;
78   wchar *Dot=GetExt(Name);
79   if (Dot!=NULL)
80     *Dot=0;
81   if (NewExt!=NULL)
82   {
83     wcsncatz(Name,L".",MaxSize);
84     wcsncatz(Name,NewExt,MaxSize);
85   }
86 }
87 
88 
89 #ifndef SFX_MODULE
SetSFXExt(wchar * SFXName,size_t MaxSize)90 void SetSFXExt(wchar *SFXName,size_t MaxSize)
91 {
92   if (SFXName==NULL || *SFXName==0)
93     return;
94 
95 #ifdef _UNIX
96   SetExt(SFXName,L"sfx",MaxSize);
97 #endif
98 
99 #if defined(_WIN_ALL) || defined(_EMX)
100   SetExt(SFXName,L"exe",MaxSize);
101 #endif
102 }
103 #endif
104 
105 
106 // 'Ext' is an extension with the leading dot, like L".rar".
GetExt(const wchar * Name)107 wchar *GetExt(const wchar *Name)
108 {
109   return Name==NULL ? NULL:wcsrchr(PointToName(Name),'.');
110 }
111 
112 
113 // 'Ext' is an extension without the leading dot, like L"rar".
CmpExt(const wchar * Name,const wchar * Ext)114 bool CmpExt(const wchar *Name,const wchar *Ext)
115 {
116   wchar *NameExt=GetExt(Name);
117   return NameExt!=NULL && wcsicomp(NameExt+1,Ext)==0;
118 }
119 
120 
IsWildcard(const wchar * Str)121 bool IsWildcard(const wchar *Str)
122 {
123   if (Str==NULL)
124     return false;
125 #ifdef _WIN_ALL
126   // Not treat the special NTFS \\?\d: path prefix as a wildcard.
127   if (Str[0]=='\\' && Str[1]=='\\' && Str[2]=='?' && Str[3]=='\\')
128     Str+=4;
129 #endif
130   return wcspbrk(Str,L"*?")!=NULL;
131 }
132 
133 
IsPathDiv(int Ch)134 bool IsPathDiv(int Ch)
135 {
136 #ifdef _WIN_ALL
137   return Ch=='\\' || Ch=='/';
138 #else
139   return Ch==CPATHDIVIDER;
140 #endif
141 }
142 
143 
IsDriveDiv(int Ch)144 bool IsDriveDiv(int Ch)
145 {
146 #ifdef _UNIX
147   return false;
148 #else
149   return Ch==':';
150 #endif
151 }
152 
153 
IsDriveLetter(const wchar * Path)154 bool IsDriveLetter(const wchar *Path)
155 {
156   wchar Letter=etoupperw(Path[0]);
157   return Letter>='A' && Letter<='Z' && IsDriveDiv(Path[1]);
158 }
159 
160 
GetPathDisk(const wchar * Path)161 int GetPathDisk(const wchar *Path)
162 {
163   if (IsDriveLetter(Path))
164     return etoupperw(*Path)-'A';
165   else
166     return -1;
167 }
168 
169 
AddEndSlash(wchar * Path,size_t MaxLength)170 void AddEndSlash(wchar *Path,size_t MaxLength)
171 {
172   size_t Length=wcslen(Path);
173   if (Length>0 && Path[Length-1]!=CPATHDIVIDER && Length+1<MaxLength)
174   {
175     Path[Length]=CPATHDIVIDER;
176     Path[Length+1]=0;
177   }
178 }
179 
180 
MakeName(const wchar * Path,const wchar * Name,wchar * Pathname,size_t MaxSize)181 void MakeName(const wchar *Path,const wchar *Name,wchar *Pathname,size_t MaxSize)
182 {
183   // 'Path', 'Name' and 'Pathname' can point to same memory area. So we use
184   // the temporary buffer instead of constructing the name in 'Pathname'.
185   wchar OutName[NM];
186   wcsncpyz(OutName,Path,ASIZE(OutName));
187   AddEndSlash(OutName,ASIZE(OutName));
188   wcsncatz(OutName,Name,ASIZE(OutName));
189   wcsncpyz(Pathname,OutName,MaxSize);
190 }
191 
192 
193 // Returns file path including the trailing path separator symbol.
GetFilePath(const wchar * FullName,wchar * Path,size_t MaxLength)194 void GetFilePath(const wchar *FullName,wchar *Path,size_t MaxLength)
195 {
196   if (MaxLength==0)
197     return;
198   size_t PathLength=Min(MaxLength-1,size_t(PointToName(FullName)-FullName));
199   wcsncpy(Path,FullName,PathLength);
200   Path[PathLength]=0;
201 }
202 
203 
204 // Removes name and returns file path without the trailing
205 // path separator symbol.
RemoveNameFromPath(wchar * Path)206 void RemoveNameFromPath(wchar *Path)
207 {
208   wchar *Name=PointToName(Path);
209   if (Name>=Path+2 && (!IsDriveDiv(Path[1]) || Name>=Path+4))
210     Name--;
211   *Name=0;
212 }
213 
214 
215 #if defined(_WIN_ALL) && !defined(SFX_MODULE)
GetAppDataPath(wchar * Path,size_t MaxSize,bool Create)216 bool GetAppDataPath(wchar *Path,size_t MaxSize,bool Create)
217 {
218   LPMALLOC g_pMalloc;
219   SHGetMalloc(&g_pMalloc);
220   LPITEMIDLIST ppidl;
221   *Path=0;
222   bool Success=false;
223   if (SHGetSpecialFolderLocation(NULL,CSIDL_APPDATA,&ppidl)==NOERROR &&
224       SHGetPathFromIDList(ppidl,Path) && *Path!=0)
225   {
226     AddEndSlash(Path,MaxSize);
227     wcsncatz(Path,L"WinRAR",MaxSize);
228     Success=FileExist(Path);
229     if (!Success && Create)
230       Success=MakeDir(Path,false,0)==MKDIR_SUCCESS;
231   }
232   g_pMalloc->Free(ppidl);
233   return Success;
234 }
235 #endif
236 
237 
238 #if defined(_WIN_ALL) && !defined(SFX_MODULE)
GetRarDataPath(wchar * Path,size_t MaxSize,bool Create)239 void GetRarDataPath(wchar *Path,size_t MaxSize,bool Create)
240 {
241   *Path=0;
242 
243   HKEY hKey;
244   if (RegOpenKeyEx(HKEY_CURRENT_USER,L"Software\\WinRAR\\Paths",0,
245                    KEY_QUERY_VALUE,&hKey)==ERROR_SUCCESS)
246   {
247     DWORD DataSize=(DWORD)MaxSize,Type;
248     RegQueryValueEx(hKey,L"AppData",0,&Type,(BYTE *)Path,&DataSize);
249     RegCloseKey(hKey);
250   }
251 
252   if (*Path==0 || !FileExist(Path))
253     if (!GetAppDataPath(Path,MaxSize,Create))
254     {
255       GetModuleFileName(NULL,Path,(DWORD)MaxSize);
256       RemoveNameFromPath(Path);
257     }
258 }
259 #endif
260 
261 
262 #ifndef SFX_MODULE
EnumConfigPaths(uint Number,wchar * Path,size_t MaxSize,bool Create)263 bool EnumConfigPaths(uint Number,wchar *Path,size_t MaxSize,bool Create)
264 {
265 #ifdef _UNIX
266   static const wchar *ConfPath[]={
267     L"/etc", L"/etc/rar", L"/usr/lib", L"/usr/local/lib", L"/usr/local/etc"
268   };
269   if (Number==0)
270   {
271     char *EnvStr=getenv("HOME");
272     if (EnvStr!=NULL)
273       CharToWide(EnvStr,Path,MaxSize);
274     else
275       wcsncpyz(Path,ConfPath[0],MaxSize);
276     return true;
277   }
278   Number--;
279   if (Number>=ASIZE(ConfPath))
280     return false;
281   wcsncpyz(Path,ConfPath[Number], MaxSize);
282   return true;
283 #elif defined(_WIN_ALL)
284   if (Number>1)
285     return false;
286   if (Number==0)
287     GetRarDataPath(Path,MaxSize,Create);
288   else
289   {
290     GetModuleFileName(NULL,Path,(DWORD)MaxSize);
291     RemoveNameFromPath(Path);
292   }
293   return true;
294 #else
295   return false;
296 #endif
297 }
298 #endif
299 
300 
301 #ifndef SFX_MODULE
GetConfigName(const wchar * Name,wchar * FullName,size_t MaxSize,bool CheckExist,bool Create)302 void GetConfigName(const wchar *Name,wchar *FullName,size_t MaxSize,bool CheckExist,bool Create)
303 {
304   *FullName=0;
305   for (uint I=0;EnumConfigPaths(I,FullName,MaxSize,Create);I++)
306   {
307     AddEndSlash(FullName,MaxSize);
308     wcsncatz(FullName,Name,MaxSize);
309     if (!CheckExist || WildFileExist(FullName))
310       break;
311   }
312 }
313 #endif
314 
315 
316 // Returns a pointer to rightmost digit of volume number or to beginning
317 // of file name if numeric part is missing.
GetVolNumPart(const wchar * ArcName)318 wchar* GetVolNumPart(const wchar *ArcName)
319 {
320   if (*ArcName==0)
321     return (wchar *)ArcName;
322 
323   // Pointing to last name character.
324   const wchar *ChPtr=ArcName+wcslen(ArcName)-1;
325 
326   // Skipping the archive extension.
327   while (!IsDigit(*ChPtr) && ChPtr>ArcName)
328     ChPtr--;
329 
330   // Skipping the numeric part of name.
331   const wchar *NumPtr=ChPtr;
332   while (IsDigit(*NumPtr) && NumPtr>ArcName)
333     NumPtr--;
334 
335   // Searching for first numeric part in names like name.part##of##.rar.
336   // Stop search on the first dot.
337   while (NumPtr>ArcName && *NumPtr!='.')
338   {
339     if (IsDigit(*NumPtr))
340     {
341       // Validate the first numeric part only if it has a dot somewhere
342       // before it.
343       wchar *Dot=wcschr(PointToName(ArcName),'.');
344       if (Dot!=NULL && Dot<NumPtr)
345         ChPtr=NumPtr;
346       break;
347     }
348     NumPtr--;
349   }
350   return (wchar *)ChPtr;
351 }
352 
353 
NextVolumeName(wchar * ArcName,uint MaxLength,bool OldNumbering)354 void NextVolumeName(wchar *ArcName,uint MaxLength,bool OldNumbering)
355 {
356   wchar *ChPtr;
357   if ((ChPtr=GetExt(ArcName))==NULL)
358   {
359     wcsncatz(ArcName,L".rar",MaxLength);
360     ChPtr=GetExt(ArcName);
361   }
362   else
363     if (ChPtr[1]==0 || wcsicomp(ChPtr,L".exe")==0 || wcsicomp(ChPtr,L".sfx")==0)
364       wcsncpyz(ChPtr,L".rar",MaxLength-(ChPtr-ArcName));
365 
366   if (ChPtr==NULL || *ChPtr!='.' || ChPtr[1]==0)
367   {
368     // Normally we shall have some extension here. If we don't, it means
369     // the name has no extension and buffer has no free space to append one.
370     // Let's clear the name to prevent a new call with same name and return.
371     *ArcName=0;
372     return;
373   }
374 
375   if (!OldNumbering)
376   {
377     ChPtr=GetVolNumPart(ArcName);
378 
379     // We should not check for IsDigit(*ChPtr) here and should increment
380     // even non-digits. If we got a corrupt archive with volume flag,
381     // but without numeric part, we still need to modify its name somehow,
382     // so while (exist(name)) {NextVolumeName()} loops do not run infinitely.
383     while ((++(*ChPtr))=='9'+1)
384     {
385       *ChPtr='0';
386       ChPtr--;
387       if (ChPtr<ArcName || !IsDigit(*ChPtr))
388       {
389         // Convert .part:.rar (.part9.rar after increment) to part10.rar.
390         for (wchar *EndPtr=ArcName+wcslen(ArcName);EndPtr!=ChPtr;EndPtr--)
391           *(EndPtr+1)=*EndPtr;
392         *(ChPtr+1)='1';
393         break;
394       }
395     }
396   }
397   else
398     if (!IsDigit(ChPtr[2]) || !IsDigit(ChPtr[3]))
399       wcsncpyz(ChPtr+2,L"00",MaxLength-(ChPtr-ArcName)-2); // From .rar to .r00.
400     else
401     {
402       ChPtr+=wcslen(ChPtr)-1; // Set to last character.
403       while (++(*ChPtr)=='9'+1)
404         if (ChPtr<=ArcName || *(ChPtr-1)=='.')
405         {
406           *ChPtr='a'; // From .999 to .a00 if started from .001 or for too short names.
407           break;
408         }
409         else
410         {
411           *ChPtr='0';
412           ChPtr--;
413         }
414     }
415 }
416 
417 
IsNameUsable(const wchar * Name)418 bool IsNameUsable(const wchar *Name)
419 {
420 #ifndef _UNIX
421   if (Name[0] && Name[1] && wcschr(Name+2,':')!=NULL)
422     return false;
423   for (const wchar *s=Name;*s!=0;s++)
424   {
425     if ((uint)*s<32)
426       return false;
427     if ((*s==' ' || *s=='.') && IsPathDiv(s[1]))
428       return false;
429   }
430 #endif
431   return *Name!=0 && wcspbrk(Name,L"?*<>|\"")==NULL;
432 }
433 
434 
MakeNameUsable(char * Name,bool Extended)435 void MakeNameUsable(char *Name,bool Extended)
436 {
437 #ifdef _WIN_ALL
438   // In Windows we also need to convert characters not defined in current
439   // code page. This double conversion changes them to '?', which is
440   // catched by code below.
441   size_t NameLength=strlen(Name);
442   wchar NameW[NM];
443   CharToWide(Name,NameW,ASIZE(NameW));
444   WideToChar(NameW,Name,NameLength+1);
445   Name[NameLength]=0;
446 #endif
447   for (char *s=Name;*s!=0;s=charnext(s))
448   {
449     if (strchr(Extended ? "?*<>|\"":"?*",*s)!=NULL || Extended && (byte)*s<32)
450       *s='_';
451 #ifdef _EMX
452     if (*s=='=')
453       *s='_';
454 #endif
455 #ifndef _UNIX
456     if (s-Name>1 && *s==':')
457       *s='_';
458     // Remove ' ' and '.' before path separator, but allow .\ and ..\.
459     if ((*s==' ' || *s=='.' && s>Name && !IsPathDiv(s[-1]) && s[-1]!='.') && IsPathDiv(s[1]))
460       *s='_';
461 #endif
462   }
463 }
464 
465 
MakeNameUsable(wchar * Name,bool Extended)466 void MakeNameUsable(wchar *Name,bool Extended)
467 {
468   for (wchar *s=Name;*s!=0;s++)
469   {
470     if (wcschr(Extended ? L"?*<>|\"":L"?*",*s)!=NULL || Extended && (uint)*s<32)
471       *s='_';
472 #ifndef _UNIX
473     if (s-Name>1 && *s==':')
474       *s='_';
475 #if 0  // We already can create such files.
476     // Remove ' ' and '.' before path separator, but allow .\ and ..\.
477     if (IsPathDiv(s[1]) && (*s==' ' || *s=='.' && s>Name &&
478         !IsPathDiv(s[-1]) && (s[-1]!='.' || s>Name+1 && !IsPathDiv(s[-2]))))
479       *s='_';
480 #endif
481 #endif
482   }
483 }
484 
485 
UnixSlashToDos(const char * SrcName,char * DestName,size_t MaxLength)486 void UnixSlashToDos(const char *SrcName,char *DestName,size_t MaxLength)
487 {
488   size_t Copied=0;
489   for (;Copied<MaxLength-1 && SrcName[Copied]!=0;Copied++)
490     DestName[Copied]=SrcName[Copied]=='/' ? '\\':SrcName[Copied];
491   DestName[Copied]=0;
492 }
493 
494 
DosSlashToUnix(const char * SrcName,char * DestName,size_t MaxLength)495 void DosSlashToUnix(const char *SrcName,char *DestName,size_t MaxLength)
496 {
497   size_t Copied=0;
498   for (;Copied<MaxLength-1 && SrcName[Copied]!=0;Copied++)
499     DestName[Copied]=SrcName[Copied]=='\\' ? '/':SrcName[Copied];
500   DestName[Copied]=0;
501 }
502 
503 
UnixSlashToDos(const wchar * SrcName,wchar * DestName,size_t MaxLength)504 void UnixSlashToDos(const wchar *SrcName,wchar *DestName,size_t MaxLength)
505 {
506   size_t Copied=0;
507   for (;Copied<MaxLength-1 && SrcName[Copied]!=0;Copied++)
508     DestName[Copied]=SrcName[Copied]=='/' ? '\\':SrcName[Copied];
509   DestName[Copied]=0;
510 }
511 
512 
DosSlashToUnix(const wchar * SrcName,wchar * DestName,size_t MaxLength)513 void DosSlashToUnix(const wchar *SrcName,wchar *DestName,size_t MaxLength)
514 {
515   size_t Copied=0;
516   for (;Copied<MaxLength-1 && SrcName[Copied]!=0;Copied++)
517     DestName[Copied]=SrcName[Copied]=='\\' ? '/':SrcName[Copied];
518   DestName[Copied]=0;
519 }
520 
521 
ConvertNameToFull(const wchar * Src,wchar * Dest,size_t MaxSize)522 void ConvertNameToFull(const wchar *Src,wchar *Dest,size_t MaxSize)
523 {
524   if (Src==NULL || *Src==0)
525   {
526     if (MaxSize>0)
527       *Dest=0;
528     return;
529   }
530 #ifdef _WIN_ALL
531   {
532     wchar FullName[NM],*NamePtr;
533     DWORD Code=GetFullPathName(Src,ASIZE(FullName),FullName,&NamePtr);
534     if (Code==0 || Code>ASIZE(FullName))
535     {
536       wchar LongName[NM];
537       if (GetWinLongPath(Src,LongName,ASIZE(LongName)))
538         Code=GetFullPathName(LongName,ASIZE(FullName),FullName,&NamePtr);
539     }
540     if (Code!=0 && Code<ASIZE(FullName))
541       wcsncpyz(Dest,FullName,MaxSize);
542     else
543       if (Src!=Dest)
544         wcsncpyz(Dest,Src,MaxSize);
545   }
546 #elif defined(_UNIX)
547   if (IsFullPath(Src))
548     *Dest=0;
549   else
550   {
551     char CurDirA[NM];
552     if (getcwd(CurDirA,ASIZE(CurDirA))==NULL)
553       *CurDirA=0;
554     CharToWide(CurDirA,Dest,MaxSize);
555     AddEndSlash(Dest,MaxSize);
556   }
557   wcsncatz(Dest,Src,MaxSize);
558 #else
559   wcsncpyz(Dest,Src,MaxSize);
560 #endif
561 }
562 
563 
IsFullPath(const wchar * Path)564 bool IsFullPath(const wchar *Path)
565 {
566 /*
567   wchar PathOnly[NM];
568   GetFilePath(Path,PathOnly,ASIZE(PathOnly));
569   if (IsWildcard(PathOnly))
570     return true;
571 */
572 #if defined(_WIN_ALL) || defined(_EMX)
573   return Path[0]=='\\' && Path[1]=='\\' || IsDriveLetter(Path) && IsPathDiv(Path[2]);
574 #else
575   return IsPathDiv(Path[0]);
576 #endif
577 }
578 
579 
IsFullRootPath(const wchar * Path)580 bool IsFullRootPath(const wchar *Path)
581 {
582   return IsFullPath(Path) || IsPathDiv(Path[0]);
583 }
584 
585 
GetPathRoot(const wchar * Path,wchar * Root,size_t MaxSize)586 void GetPathRoot(const wchar *Path,wchar *Root,size_t MaxSize)
587 {
588   *Root=0;
589   if (IsDriveLetter(Path))
590     swprintf(Root,MaxSize,L"%c:\\",*Path);
591   else
592     if (Path[0]=='\\' && Path[1]=='\\')
593     {
594       const wchar *Slash=wcschr(Path+2,'\\');
595       if (Slash!=NULL)
596       {
597         size_t Length;
598         if ((Slash=wcschr(Slash+1,'\\'))!=NULL)
599           Length=Slash-Path+1;
600         else
601           Length=wcslen(Path);
602         if (Length>=MaxSize)
603           Length=0;
604         wcsncpy(Root,Path,Length);
605         Root[Length]=0;
606       }
607     }
608 }
609 
610 
ParseVersionFileName(wchar * Name,bool Truncate)611 int ParseVersionFileName(wchar *Name,bool Truncate)
612 {
613   int Version=0;
614   wchar *VerText=wcsrchr(Name,';');
615   if (VerText!=NULL)
616   {
617     Version=atoiw(VerText+1);
618     if (Truncate)
619       *VerText=0;
620   }
621   return Version;
622 }
623 
624 
625 #if !defined(SFX_MODULE)
626 // Get the name of first volume. Return the leftmost digit of volume number.
VolNameToFirstName(const wchar * VolName,wchar * FirstName,size_t MaxSize,bool NewNumbering)627 wchar* VolNameToFirstName(const wchar *VolName,wchar *FirstName,size_t MaxSize,bool NewNumbering)
628 {
629   if (FirstName!=VolName)
630     wcsncpyz(FirstName,VolName,MaxSize);
631   wchar *VolNumStart=FirstName;
632   if (NewNumbering)
633   {
634     wchar N='1';
635 
636     // From the rightmost digit of volume number to the left.
637     for (wchar *ChPtr=GetVolNumPart(FirstName);ChPtr>FirstName;ChPtr--)
638       if (IsDigit(*ChPtr))
639       {
640         *ChPtr=N; // Set the rightmost digit to '1' and others to '0'.
641         N='0';
642       }
643       else
644         if (N=='0')
645         {
646           VolNumStart=ChPtr+1; // Store the position of leftmost digit in volume number.
647           break;
648         }
649   }
650   else
651   {
652     // Old volume numbering scheme. Just set the extension to ".rar".
653     SetExt(FirstName,L"rar",MaxSize);
654     VolNumStart=GetExt(FirstName);
655   }
656   if (!FileExist(FirstName))
657   {
658     // If the first volume, which name we just generated, is not exist,
659     // check if volume with same name and any other extension is available.
660     // It can help in case of *.exe or *.sfx first volume.
661     wchar Mask[NM];
662     wcsncpyz(Mask,FirstName,ASIZE(Mask));
663     SetExt(Mask,L"*",ASIZE(Mask));
664     FindFile Find;
665     Find.SetMask(Mask);
666     FindData FD;
667     while (Find.Next(&FD))
668     {
669       Archive Arc;
670       if (Arc.Open(FD.Name,0) && Arc.IsArchive(true) && Arc.FirstVolume)
671       {
672         wcsncpyz(FirstName,FD.Name,MaxSize);
673         break;
674       }
675     }
676   }
677   return VolNumStart;
678 }
679 #endif
680 
681 
682 #ifndef SFX_MODULE
GenArcName(wchar * ArcName,size_t MaxSize,const wchar * GenerateMask,uint ArcNumber,bool & ArcNumPresent)683 static void GenArcName(wchar *ArcName,size_t MaxSize,const wchar *GenerateMask,uint ArcNumber,bool &ArcNumPresent)
684 {
685   bool Prefix=false;
686   if (*GenerateMask=='+')
687   {
688     Prefix=true;    // Add the time string before the archive name.
689     GenerateMask++; // Skip '+' in the beginning of time mask.
690   }
691 
692   wchar Mask[MAX_GENERATE_MASK];
693   wcsncpyz(Mask,*GenerateMask!=0 ? GenerateMask:L"yyyymmddhhmmss",ASIZE(Mask));
694 
695   bool QuoteMode=false,Hours=false;
696   for (uint I=0;Mask[I]!=0;I++)
697   {
698     if (Mask[I]=='{' || Mask[I]=='}')
699     {
700       QuoteMode=(Mask[I]=='{');
701       continue;
702     }
703     if (QuoteMode)
704       continue;
705     int CurChar=toupperw(Mask[I]);
706     if (CurChar=='H')
707       Hours=true;
708 
709     if (Hours && CurChar=='M')
710     {
711       // Replace minutes with 'I'. We use 'M' both for months and minutes,
712       // so we treat as minutes only those 'M' which are found after hours.
713       Mask[I]='I';
714     }
715     if (CurChar=='N')
716     {
717       uint Digits=GetDigits(ArcNumber);
718       uint NCount=0;
719       while (toupperw(Mask[I+NCount])=='N')
720         NCount++;
721 
722       // Here we ensure that we have enough 'N' characters to fit all digits
723       // of archive number. We'll replace them by actual number later
724       // in this function.
725       if (NCount<Digits)
726       {
727         wmemmove(Mask+I+Digits,Mask+I+NCount,wcslen(Mask+I+NCount)+1);
728         wmemset(Mask+I,'N',Digits);
729       }
730       I+=Max(Digits,NCount)-1;
731       ArcNumPresent=true;
732       continue;
733     }
734   }
735 
736   RarTime CurTime;
737   CurTime.SetCurrentTime();
738   RarLocalTime rlt;
739   CurTime.GetLocal(&rlt);
740 
741   wchar Ext[NM],*Dot=GetExt(ArcName);
742   *Ext=0;
743   if (Dot==NULL)
744     wcsncpyz(Ext,*PointToName(ArcName)==0 ? L".rar":L"",ASIZE(Ext));
745   else
746   {
747     wcsncpyz(Ext,Dot,ASIZE(Ext));
748     *Dot=0;
749   }
750 
751   int WeekDay=rlt.wDay==0 ? 6:rlt.wDay-1;
752   int StartWeekDay=rlt.yDay-WeekDay;
753   if (StartWeekDay<0)
754     if (StartWeekDay<=-4)
755       StartWeekDay+=IsLeapYear(rlt.Year-1) ? 366:365;
756     else
757       StartWeekDay=0;
758   int CurWeek=StartWeekDay/7+1;
759   if (StartWeekDay%7>=4)
760     CurWeek++;
761 
762   char Field[10][6];
763 
764   sprintf(Field[0],"%04u",rlt.Year);
765   sprintf(Field[1],"%02u",rlt.Month);
766   sprintf(Field[2],"%02u",rlt.Day);
767   sprintf(Field[3],"%02u",rlt.Hour);
768   sprintf(Field[4],"%02u",rlt.Minute);
769   sprintf(Field[5],"%02u",rlt.Second);
770   sprintf(Field[6],"%02u",(uint)CurWeek);
771   sprintf(Field[7],"%u",(uint)WeekDay+1);
772   sprintf(Field[8],"%03u",rlt.yDay+1);
773   sprintf(Field[9],"%05u",ArcNumber);
774 
775   const wchar *MaskChars=L"YMDHISWAEN";
776 
777   int CField[sizeof(Field)/sizeof(Field[0])];
778   memset(CField,0,sizeof(CField));
779   QuoteMode=false;
780   for (uint I=0;Mask[I]!=0;I++)
781   {
782     if (Mask[I]=='{' || Mask[I]=='}')
783     {
784       QuoteMode=(Mask[I]=='{');
785       continue;
786     }
787     if (QuoteMode)
788       continue;
789     const wchar *ChPtr=wcschr(MaskChars,toupperw(Mask[I]));
790     if (ChPtr!=NULL)
791       CField[ChPtr-MaskChars]++;
792    }
793 
794   wchar DateText[MAX_GENERATE_MASK];
795   *DateText=0;
796   QuoteMode=false;
797   for (size_t I=0,J=0;Mask[I]!=0 && J<ASIZE(DateText)-1;I++)
798   {
799     if (Mask[I]=='{' || Mask[I]=='}')
800     {
801       QuoteMode=(Mask[I]=='{');
802       continue;
803     }
804     const wchar *ChPtr=wcschr(MaskChars,toupperw(Mask[I]));
805     if (ChPtr==NULL || QuoteMode)
806     {
807       DateText[J]=Mask[I];
808 #ifdef _WIN_ALL
809       // We do not allow ':' in Windows because of NTFS streams.
810       // Users had problems after specifying hh:mm mask.
811       if (DateText[J]==':')
812         DateText[J]='_';
813 #endif
814     }
815     else
816     {
817       size_t FieldPos=ChPtr-MaskChars;
818       int CharPos=(int)strlen(Field[FieldPos])-CField[FieldPos]--;
819       if (FieldPos==1 && toupperw(Mask[I+1])=='M' && toupperw(Mask[I+2])=='M')
820       {
821         wcsncpyz(DateText+J,GetMonthName(rlt.Month-1),ASIZE(DateText)-J);
822         J=wcslen(DateText);
823         I+=2;
824         continue;
825       }
826       if (CharPos<0)
827         DateText[J]=Mask[I];
828       else
829         DateText[J]=Field[FieldPos][CharPos];
830     }
831     DateText[++J]=0;
832   }
833 
834   if (Prefix)
835   {
836     wchar NewName[NM];
837     GetFilePath(ArcName,NewName,ASIZE(NewName));
838     AddEndSlash(NewName,ASIZE(NewName));
839     wcsncatz(NewName,DateText,ASIZE(NewName));
840     wcsncatz(NewName,PointToName(ArcName),ASIZE(NewName));
841     wcsncpyz(ArcName,NewName,MaxSize);
842   }
843   else
844     wcsncatz(ArcName,DateText,MaxSize);
845   wcsncatz(ArcName,Ext,MaxSize);
846 }
847 
848 
GenerateArchiveName(wchar * ArcName,size_t MaxSize,const wchar * GenerateMask,bool Archiving)849 void GenerateArchiveName(wchar *ArcName,size_t MaxSize,const wchar *GenerateMask,bool Archiving)
850 {
851   wchar NewName[NM];
852 
853   uint ArcNumber=1;
854   while (true) // Loop for 'N' (archive number) processing.
855   {
856     wcsncpyz(NewName,ArcName,ASIZE(NewName));
857 
858     bool ArcNumPresent=false;
859 
860     GenArcName(NewName,ASIZE(NewName),GenerateMask,ArcNumber,ArcNumPresent);
861 
862     if (!ArcNumPresent)
863       break;
864     if (!FileExist(NewName))
865     {
866       if (!Archiving && ArcNumber>1)
867       {
868         // If we perform non-archiving operation, we need to use the last
869         // existing archive before the first unused name. So we generate
870         // the name for (ArcNumber-1) below.
871         wcsncpyz(NewName,NullToEmpty(ArcName),ASIZE(NewName));
872         GenArcName(NewName,ASIZE(NewName),GenerateMask,ArcNumber-1,ArcNumPresent);
873       }
874       break;
875     }
876     ArcNumber++;
877   }
878   wcsncpyz(ArcName,NewName,MaxSize);
879 }
880 #endif
881 
882 
GetWideName(const char * Name,const wchar * NameW,wchar * DestW,size_t DestSize)883 wchar* GetWideName(const char *Name,const wchar *NameW,wchar *DestW,size_t DestSize)
884 {
885   if (NameW!=NULL && *NameW!=0)
886   {
887     if (DestW!=NameW)
888       wcsncpy(DestW,NameW,DestSize);
889   }
890   else
891     if (Name!=NULL)
892       CharToWide(Name,DestW,DestSize);
893     else
894       *DestW=0;
895 
896   // Ensure that we return a zero terminate string for security reasons.
897   if (DestSize>0)
898     DestW[DestSize-1]=0;
899 
900   return DestW;
901 }
902 
903 
904 #ifdef _WIN_ALL
905 // We should return 'true' even if resulting path is shorter than MAX_PATH,
906 // because we can also use this function to open files with non-standard
907 // characters, even if their path length is normal.
GetWinLongPath(const wchar * Src,wchar * Dest,size_t MaxSize)908 bool GetWinLongPath(const wchar *Src,wchar *Dest,size_t MaxSize)
909 {
910   if (*Src==0)
911     return false;
912   const wchar *Prefix=L"\\\\?\\";
913   const size_t PrefixLength=4;
914   bool FullPath=IsDriveLetter(Src) && IsPathDiv(Src[2]);
915   size_t SrcLength=wcslen(Src);
916   if (IsFullPath(Src)) // Paths in d:\path\name format.
917   {
918     if (IsDriveLetter(Src))
919     {
920       if (MaxSize<=PrefixLength+SrcLength)
921         return false;
922       wcsncpyz(Dest,Prefix,MaxSize);
923       wcsncatz(Dest,Src,MaxSize); // "\\?\D:\very long path".
924       return true;
925     }
926     else
927       if (Src[0]=='\\' && Src[1]=='\\')
928       {
929         if (MaxSize<=PrefixLength+SrcLength+2)
930           return false;
931         wcsncpyz(Dest,Prefix,MaxSize);
932         wcsncatz(Dest,L"UNC",MaxSize);
933         wcsncatz(Dest,Src+1,MaxSize); // "\\?\UNC\server\share".
934         return true;
935       }
936     // We may be here only if we modify IsFullPath in the future.
937     return false;
938   }
939   else
940   {
941     wchar CurDir[NM];
942     DWORD DirCode=GetCurrentDirectory(ASIZE(CurDir)-1,CurDir);
943     if (DirCode==0 || DirCode>ASIZE(CurDir)-1)
944       return false;
945 
946     if (IsPathDiv(Src[0])) // Paths in \path\name format.
947     {
948       if (MaxSize<=PrefixLength+SrcLength+2)
949         return false;
950       wcsncpyz(Dest,Prefix,MaxSize);
951       CurDir[2]=0;
952       wcsncatz(Dest,CurDir,MaxSize); // Copy drive letter 'd:'.
953       wcsncatz(Dest,Src,MaxSize);
954       return true;
955     }
956     else  // Paths in path\name format.
957     {
958       AddEndSlash(CurDir,ASIZE(CurDir));
959       if (MaxSize<=PrefixLength+wcslen(CurDir)+SrcLength)
960         return false;
961       wcsncpyz(Dest,Prefix,MaxSize);
962       wcsncatz(Dest,CurDir,MaxSize);
963 
964       if (Src[0]=='.' && IsPathDiv(Src[1])) // Remove leading .\ in pathname.
965         Src+=2;
966 
967       wcsncatz(Dest,Src,MaxSize);
968       return true;
969     }
970   }
971   return false;
972 }
973 
974 
975 // Convert Unix, OS X and Android decomposed chracters to Windows precomposed.
ConvertToPrecomposed(wchar * Name,size_t NameSize)976 void ConvertToPrecomposed(wchar *Name,size_t NameSize)
977 {
978   wchar FileName[NM];
979   if (WinNT()>=WNT_VISTA && // MAP_PRECOMPOSED is not supported in XP.
980       FoldString(MAP_PRECOMPOSED,Name,-1,FileName,ASIZE(FileName))!=0)
981   {
982     FileName[ASIZE(FileName)-1]=0;
983     wcsncpyz(Name,FileName,NameSize);
984   }
985 }
986 
987 
988 // Remove trailing spaces and dots in file name and in dir names in path.
MakeNameCompatible(wchar * Name)989 void MakeNameCompatible(wchar *Name)
990 {
991   int Src=0,Dest=0;
992   while (true)
993   {
994     if (IsPathDiv(Name[Src]) || Name[Src]==0)
995       for (int I=Dest-1;I>0 && (Name[I]==' ' || Name[I]=='.');I--)
996       {
997         // Permit path1/./path2 and ../path1 paths.
998         if (Name[I]=='.' && (IsPathDiv(Name[I-1]) || Name[I-1]=='.' && I==1))
999           break;
1000         Dest--;
1001       }
1002     Name[Dest]=Name[Src];
1003     if (Name[Src]==0)
1004       break;
1005     Src++;
1006     Dest++;
1007   }
1008 }
1009 #endif
1010