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