pathfn.cpp 27 KB

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