"Fossies" - the Fresh Open Source Software Archive 
Member "unrar/pathfn.cpp" (4 May 2022, 27127 Bytes) of package /linux/misc/unrarsrc-6.1.7.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "pathfn.cpp" see the
Fossies "Dox" file reference documentation and the last
Fossies "Diffs" side-by-side code changes report:
6.1.4_vs_6.1.5.
1 #include "rar.hpp"
2
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!=0 && IsDriveDiv(Path[1])) ? Path+2:Path);
9 }
10
11
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
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
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
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
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".
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".
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
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
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
144 bool IsDriveDiv(int Ch)
145 {
146 #ifdef _UNIX
147 return false;
148 #else
149 return Ch==':';
150 #endif
151 }
152
153
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
161 int GetPathDisk(const wchar *Path)
162 {
163 if (IsDriveLetter(Path))
164 return etoupperw(*Path)-'A';
165 else
166 return -1;
167 }
168
169
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
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 // 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.
196 void 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.
208 void RemoveNameFromPath(wchar *Path)
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)
218 bool 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)
241 void 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
265 bool 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
304 void 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.
320 wchar* 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
359 void 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
423 bool 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
440 void 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
471 void 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
491 void 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
500 void 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
509 void 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
518 void 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
527 void 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
569 bool 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
585 bool IsFullRootPath(const wchar *Path)
586 {
587 return IsFullPath(Path) || IsPathDiv(Path[0]);
588 }
589
590
591 void 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
616 int 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.
632 wchar* 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
688 static 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
697 wchar Mask[MAX_GENERATE_MASK];
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
869 void 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
903 wchar* 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.
928 bool 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.
996 void 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
1008 void 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