unrarsrc  6.1.7
About: unrar extracts, views and tests the contents of archives created with the RAR archiver.
  Fossies Dox: unrarsrc-6.1.7.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

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