xref: /aosp_15_r20/external/lzma/CPP/Windows/FileFind.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1 // Windows/FileFind.cpp
2 
3 #include "StdAfx.h"
4 
5 // #include <stdio.h>
6 
7 #ifndef _WIN32
8 #include <fcntl.h>           /* Definition of AT_* constants */
9 #include "TimeUtils.h"
10 // for major
11 // #include <sys/sysmacros.h>
12 #endif
13 
14 #include "FileFind.h"
15 #include "FileIO.h"
16 #include "FileName.h"
17 
18 #ifndef _UNICODE
19 extern bool g_IsNT;
20 #endif
21 
22 using namespace NWindows;
23 using namespace NFile;
24 using namespace NName;
25 
26 #if defined(_WIN32) && !defined(UNDER_CE)
27 
28 #if !defined(Z7_WIN32_WINNT_MIN) || Z7_WIN32_WINNT_MIN < 0x0502  // Win2003
29 #define Z7_USE_DYN_FindFirstStream
30 #endif
31 
32 #ifdef Z7_USE_DYN_FindFirstStream
33 
34 Z7_DIAGNOSTIC_IGNORE_CAST_FUNCTION
35 
36 EXTERN_C_BEGIN
37 
38 typedef enum
39 {
40   My_FindStreamInfoStandard,
41   My_FindStreamInfoMaxInfoLevel
42 } MY_STREAM_INFO_LEVELS;
43 
44 typedef struct
45 {
46   LARGE_INTEGER StreamSize;
47   WCHAR cStreamName[MAX_PATH + 36];
48 } MY_WIN32_FIND_STREAM_DATA, *MY_PWIN32_FIND_STREAM_DATA;
49 
50 typedef HANDLE (WINAPI *Func_FindFirstStreamW)(LPCWSTR fileName, MY_STREAM_INFO_LEVELS infoLevel,
51     LPVOID findStreamData, DWORD flags);
52 
53 typedef BOOL (APIENTRY *Func_FindNextStreamW)(HANDLE findStream, LPVOID findStreamData);
54 
55 EXTERN_C_END
56 
57 #else
58 
59 #define MY_WIN32_FIND_STREAM_DATA  WIN32_FIND_STREAM_DATA
60 #define My_FindStreamInfoStandard  FindStreamInfoStandard
61 
62 #endif
63 #endif // defined(_WIN32) && !defined(UNDER_CE)
64 
65 
66 namespace NWindows {
67 namespace NFile {
68 
69 
70 #ifdef _WIN32
71 #ifdef Z7_DEVICE_FILE
72 namespace NSystem
73 {
74 bool MyGetDiskFreeSpace(CFSTR rootPath, UInt64 &clusterSize, UInt64 &totalSize, UInt64 &freeSize);
75 }
76 #endif
77 #endif
78 
79 namespace NFind {
80 
81 /*
82 #ifdef _WIN32
83 #define MY_CLEAR_FILETIME(ft) ft.dwLowDateTime = ft.dwHighDateTime = 0;
84 #else
85 #define MY_CLEAR_FILETIME(ft) ft.tv_sec = 0;  ft.tv_nsec = 0;
86 #endif
87 */
88 
ClearBase()89 void CFileInfoBase::ClearBase() throw()
90 {
91   Size = 0;
92   FiTime_Clear(CTime);
93   FiTime_Clear(ATime);
94   FiTime_Clear(MTime);
95 
96  #ifdef _WIN32
97   Attrib = 0;
98   // ReparseTag = 0;
99   IsAltStream = false;
100   IsDevice = false;
101  #else
102   dev = 0;
103   ino = 0;
104   mode = 0;
105   nlink = 0;
106   uid = 0;
107   gid = 0;
108   rdev = 0;
109  #endif
110 }
111 
112 
SetAs_StdInFile()113 bool CFileInfoBase::SetAs_StdInFile()
114 {
115   ClearBase();
116   Size = (UInt64)(Int64)-1;
117   NTime::GetCurUtc_FiTime(MTime);
118   CTime = ATime = MTime;
119 
120 #ifdef _WIN32
121 
122   /* in GUI mode : GetStdHandle(STD_INPUT_HANDLE) returns NULL,
123      and it doesn't set LastError.  */
124 #if 1
125   SetLastError(0);
126   const HANDLE h = GetStdHandle(STD_INPUT_HANDLE);
127   if (!h || h == INVALID_HANDLE_VALUE)
128   {
129     if (GetLastError() == 0)
130       SetLastError(ERROR_INVALID_HANDLE);
131     return false;
132   }
133   BY_HANDLE_FILE_INFORMATION info;
134   if (GetFileInformationByHandle(h, &info)
135       && info.dwVolumeSerialNumber)
136   {
137     Size = (((UInt64)info.nFileSizeHigh) << 32) + info.nFileSizeLow;
138     // FileID_Low = (((UInt64)info.nFileIndexHigh) << 32) + info.nFileIndexLow;
139     // NumLinks = SupportHardLinks ? info.nNumberOfLinks : 1;
140     Attrib = info.dwFileAttributes;
141     CTime = info.ftCreationTime;
142     ATime = info.ftLastAccessTime;
143     MTime = info.ftLastWriteTime;
144   }
145 #if 0
146   printf(
147     "\ndwFileAttributes = %8x"
148     "\nftCreationTime   = %8x"
149     "\nftLastAccessTime = %8x"
150     "\nftLastWriteTime  = %8x"
151     "\ndwVolumeSerialNumber  = %8x"
152     "\nnFileSizeHigh  = %8x"
153     "\nnFileSizeLow   = %8x"
154     "\nnNumberOfLinks  = %8x"
155     "\nnFileIndexHigh  = %8x"
156     "\nnFileIndexLow   = %8x \n",
157       (unsigned)info.dwFileAttributes,
158       (unsigned)info.ftCreationTime.dwHighDateTime,
159       (unsigned)info.ftLastAccessTime.dwHighDateTime,
160       (unsigned)info.ftLastWriteTime.dwHighDateTime,
161       (unsigned)info.dwVolumeSerialNumber,
162       (unsigned)info.nFileSizeHigh,
163       (unsigned)info.nFileSizeLow,
164       (unsigned)info.nNumberOfLinks,
165       (unsigned)info.nFileIndexHigh,
166       (unsigned)info.nFileIndexLow);
167 #endif
168 #endif
169 
170 #else // non-Wiondow
171 
172   mode = S_IFIFO | 0777; // 0755 : 0775 : 0664 : 0644 :
173 #if 1
174   struct stat st;
175   if (fstat(0, &st) == 0)
176   {
177     SetFrom_stat(st);
178     if (!S_ISREG(st.st_mode)
179         // S_ISFIFO(st->st_mode)
180         || st.st_size == 0)
181     {
182       Size = (UInt64)(Int64)-1;
183       // mode = S_IFIFO | 0777;
184     }
185   }
186 #endif
187 #endif
188 
189   return true;
190 }
191 
IsDots() const192 bool CFileInfo::IsDots() const throw()
193 {
194   if (!IsDir() || Name.IsEmpty())
195     return false;
196   if (Name[0] != '.')
197     return false;
198   return Name.Len() == 1 || (Name.Len() == 2 && Name[1] == '.');
199 }
200 
201 
202 #ifdef _WIN32
203 
204 
205 #define WIN_FD_TO_MY_FI(fi, fd) \
206   fi.Attrib = fd.dwFileAttributes; \
207   fi.CTime = fd.ftCreationTime; \
208   fi.ATime = fd.ftLastAccessTime; \
209   fi.MTime = fd.ftLastWriteTime; \
210   fi.Size = (((UInt64)fd.nFileSizeHigh) << 32) + fd.nFileSizeLow; \
211   /* fi.ReparseTag = fd.dwReserved0; */ \
212   fi.IsAltStream = false; \
213   fi.IsDevice = false;
214 
215   /*
216   #ifdef UNDER_CE
217   fi.ObjectID = fd.dwOID;
218   #else
219   fi.ReparseTag = fd.dwReserved0;
220   #endif
221   */
222 
Convert_WIN32_FIND_DATA_to_FileInfo(const WIN32_FIND_DATAW & fd,CFileInfo & fi)223 static void Convert_WIN32_FIND_DATA_to_FileInfo(const WIN32_FIND_DATAW &fd, CFileInfo &fi)
224 {
225   WIN_FD_TO_MY_FI(fi, fd)
226   fi.Name = us2fs(fd.cFileName);
227   #if defined(_WIN32) && !defined(UNDER_CE)
228   // fi.ShortName = us2fs(fd.cAlternateFileName);
229   #endif
230 }
231 
232 #ifndef _UNICODE
Convert_WIN32_FIND_DATA_to_FileInfo(const WIN32_FIND_DATA & fd,CFileInfo & fi)233 static void Convert_WIN32_FIND_DATA_to_FileInfo(const WIN32_FIND_DATA &fd, CFileInfo &fi)
234 {
235   WIN_FD_TO_MY_FI(fi, fd)
236   fi.Name = fas2fs(fd.cFileName);
237   #if defined(_WIN32) && !defined(UNDER_CE)
238   // fi.ShortName = fas2fs(fd.cAlternateFileName);
239   #endif
240 }
241 #endif
242 
243 ////////////////////////////////
244 // CFindFile
245 
Close()246 bool CFindFileBase::Close() throw()
247 {
248   if (_handle == INVALID_HANDLE_VALUE)
249     return true;
250   if (!::FindClose(_handle))
251     return false;
252   _handle = INVALID_HANDLE_VALUE;
253   return true;
254 }
255 
256 /*
257 WinXP-64 FindFirstFile():
258   ""      -  ERROR_PATH_NOT_FOUND
259   folder\ -  ERROR_FILE_NOT_FOUND
260   \       -  ERROR_FILE_NOT_FOUND
261   c:\     -  ERROR_FILE_NOT_FOUND
262   c:      -  ERROR_FILE_NOT_FOUND, if current dir is ROOT     ( c:\ )
263   c:      -  OK,                   if current dir is NOT ROOT ( c:\folder )
264   folder  -  OK
265 
266   \\               - ERROR_INVALID_NAME
267   \\Server         - ERROR_INVALID_NAME
268   \\Server\        - ERROR_INVALID_NAME
269 
270   \\Server\Share            - ERROR_BAD_NETPATH
271   \\Server\Share            - ERROR_BAD_NET_NAME (Win7).
272              !!! There is problem : Win7 makes some requests for "\\Server\Shar" (look in Procmon),
273                  when we call it for "\\Server\Share"
274 
275   \\Server\Share\           - ERROR_FILE_NOT_FOUND
276 
277   \\?\UNC\Server\Share      - ERROR_INVALID_NAME
278   \\?\UNC\Server\Share      - ERROR_BAD_PATHNAME (Win7)
279   \\?\UNC\Server\Share\     - ERROR_FILE_NOT_FOUND
280 
281   \\Server\Share_RootDrive  - ERROR_INVALID_NAME
282   \\Server\Share_RootDrive\ - ERROR_INVALID_NAME
283 
284   e:\* - ERROR_FILE_NOT_FOUND, if there are no items in that root folder
285   w:\* - ERROR_PATH_NOT_FOUND, if there is no such drive w:
286 */
287 
FindFirst(CFSTR path,CFileInfo & fi)288 bool CFindFile::FindFirst(CFSTR path, CFileInfo &fi)
289 {
290   if (!Close())
291     return false;
292   #ifndef _UNICODE
293   if (!g_IsNT)
294   {
295     WIN32_FIND_DATAA fd;
296     _handle = ::FindFirstFileA(fs2fas(path), &fd);
297     if (_handle == INVALID_HANDLE_VALUE)
298       return false;
299     Convert_WIN32_FIND_DATA_to_FileInfo(fd, fi);
300   }
301   else
302   #endif
303   {
304     WIN32_FIND_DATAW fd;
305 
306     IF_USE_MAIN_PATH
307       _handle = ::FindFirstFileW(fs2us(path), &fd);
308     #ifdef Z7_LONG_PATH
309     if (_handle == INVALID_HANDLE_VALUE && USE_SUPER_PATH)
310     {
311       UString superPath;
312       if (GetSuperPath(path, superPath, USE_MAIN_PATH))
313         _handle = ::FindFirstFileW(superPath, &fd);
314     }
315     #endif
316     if (_handle == INVALID_HANDLE_VALUE)
317       return false;
318     Convert_WIN32_FIND_DATA_to_FileInfo(fd, fi);
319   }
320   return true;
321 }
322 
FindNext(CFileInfo & fi)323 bool CFindFile::FindNext(CFileInfo &fi)
324 {
325   #ifndef _UNICODE
326   if (!g_IsNT)
327   {
328     WIN32_FIND_DATAA fd;
329     if (!::FindNextFileA(_handle, &fd))
330       return false;
331     Convert_WIN32_FIND_DATA_to_FileInfo(fd, fi);
332   }
333   else
334   #endif
335   {
336     WIN32_FIND_DATAW fd;
337     if (!::FindNextFileW(_handle, &fd))
338       return false;
339     Convert_WIN32_FIND_DATA_to_FileInfo(fd, fi);
340   }
341   return true;
342 }
343 
344 #if defined(_WIN32) && !defined(UNDER_CE)
345 
346 ////////////////////////////////
347 // AltStreams
348 
349 #ifdef Z7_USE_DYN_FindFirstStream
350 static Func_FindFirstStreamW g_FindFirstStreamW;
351 static Func_FindNextStreamW  g_FindNextStreamW;
352 #define MY_FindFirstStreamW  g_FindFirstStreamW
353 #define MY_FindNextStreamW   g_FindNextStreamW
354 static struct CFindStreamLoader
355 {
CFindStreamLoaderNWindows::NFile::NFind::CFindStreamLoader356   CFindStreamLoader()
357   {
358     const HMODULE hm = ::GetModuleHandleA("kernel32.dll");
359        g_FindFirstStreamW = Z7_GET_PROC_ADDRESS(
360     Func_FindFirstStreamW, hm,
361         "FindFirstStreamW");
362        g_FindNextStreamW = Z7_GET_PROC_ADDRESS(
363     Func_FindNextStreamW, hm,
364         "FindNextStreamW");
365   }
366 } g_FindStreamLoader;
367 #else
368 #define MY_FindFirstStreamW  FindFirstStreamW
369 #define MY_FindNextStreamW   FindNextStreamW
370 #endif
371 
372 
IsMainStream() const373 bool CStreamInfo::IsMainStream() const throw()
374 {
375   return StringsAreEqualNoCase_Ascii(Name, "::$DATA");
376 }
377 
GetReducedName() const378 UString CStreamInfo::GetReducedName() const
379 {
380   // remove ":$DATA" postfix, but keep postfix, if Name is "::$DATA"
381   UString s (Name);
382   if (s.Len() > 6 + 1 && StringsAreEqualNoCase_Ascii(s.RightPtr(6), ":$DATA"))
383     s.DeleteFrom(s.Len() - 6);
384   return s;
385 }
386 
387 /*
388 UString CStreamInfo::GetReducedName2() const
389 {
390   UString s = GetReducedName();
391   if (!s.IsEmpty() && s[0] == ':')
392     s.Delete(0);
393   return s;
394 }
395 */
396 
Convert_WIN32_FIND_STREAM_DATA_to_StreamInfo(const MY_WIN32_FIND_STREAM_DATA & sd,CStreamInfo & si)397 static void Convert_WIN32_FIND_STREAM_DATA_to_StreamInfo(const MY_WIN32_FIND_STREAM_DATA &sd, CStreamInfo &si)
398 {
399   si.Size = (UInt64)sd.StreamSize.QuadPart;
400   si.Name = sd.cStreamName;
401 }
402 
403 /*
404   WinXP-64 FindFirstStream():
405   ""      -  ERROR_PATH_NOT_FOUND
406   folder\ -  OK
407   folder  -  OK
408   \       -  OK
409   c:\     -  OK
410   c:      -  OK, if current dir is ROOT     ( c:\ )
411   c:      -  OK, if current dir is NOT ROOT ( c:\folder )
412   \\Server\Share   - OK
413   \\Server\Share\  - OK
414 
415   \\               - ERROR_INVALID_NAME
416   \\Server         - ERROR_INVALID_NAME
417   \\Server\        - ERROR_INVALID_NAME
418 */
419 
FindFirst(CFSTR path,CStreamInfo & si)420 bool CFindStream::FindFirst(CFSTR path, CStreamInfo &si)
421 {
422   if (!Close())
423     return false;
424 #ifdef Z7_USE_DYN_FindFirstStream
425   if (!g_FindFirstStreamW)
426   {
427     ::SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
428     return false;
429   }
430 #endif
431   {
432     MY_WIN32_FIND_STREAM_DATA sd;
433     SetLastError(0);
434     IF_USE_MAIN_PATH
435       _handle = MY_FindFirstStreamW(fs2us(path), My_FindStreamInfoStandard, &sd, 0);
436     if (_handle == INVALID_HANDLE_VALUE)
437     {
438       if (::GetLastError() == ERROR_HANDLE_EOF)
439         return false;
440       // long name can be tricky for path like ".\dirName".
441       #ifdef Z7_LONG_PATH
442       if (USE_SUPER_PATH)
443       {
444         UString superPath;
445         if (GetSuperPath(path, superPath, USE_MAIN_PATH))
446           _handle = MY_FindFirstStreamW(superPath, My_FindStreamInfoStandard, &sd, 0);
447       }
448       #endif
449     }
450     if (_handle == INVALID_HANDLE_VALUE)
451       return false;
452     Convert_WIN32_FIND_STREAM_DATA_to_StreamInfo(sd, si);
453   }
454   return true;
455 }
456 
FindNext(CStreamInfo & si)457 bool CFindStream::FindNext(CStreamInfo &si)
458 {
459 #ifdef Z7_USE_DYN_FindFirstStream
460   if (!g_FindNextStreamW)
461   {
462     ::SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
463     return false;
464   }
465 #endif
466   {
467     MY_WIN32_FIND_STREAM_DATA sd;
468     if (!MY_FindNextStreamW(_handle, &sd))
469       return false;
470     Convert_WIN32_FIND_STREAM_DATA_to_StreamInfo(sd, si);
471   }
472   return true;
473 }
474 
Next(CStreamInfo & si,bool & found)475 bool CStreamEnumerator::Next(CStreamInfo &si, bool &found)
476 {
477   bool res;
478   if (_find.IsHandleAllocated())
479     res = _find.FindNext(si);
480   else
481     res = _find.FindFirst(_filePath, si);
482   if (res)
483   {
484     found = true;
485     return true;
486   }
487   found = false;
488   return (::GetLastError() == ERROR_HANDLE_EOF);
489 }
490 
491 #endif
492 
493 
494 /*
495 WinXP-64 GetFileAttributes():
496   If the function fails, it returns INVALID_FILE_ATTRIBUTES and use GetLastError() to get error code
497 
498   \    - OK
499   C:\  - OK, if there is such drive,
500   D:\  - ERROR_PATH_NOT_FOUND, if there is no such drive,
501 
502   C:\folder     - OK
503   C:\folder\    - OK
504   C:\folderBad  - ERROR_FILE_NOT_FOUND
505 
506   \\Server\BadShare  - ERROR_BAD_NETPATH
507   \\Server\Share     - WORKS OK, but MSDN says:
508                           GetFileAttributes for a network share, the function fails, and GetLastError
509                           returns ERROR_BAD_NETPATH. You must specify a path to a subfolder on that share.
510 */
511 
GetFileAttrib(CFSTR path)512 DWORD GetFileAttrib(CFSTR path)
513 {
514   #ifndef _UNICODE
515   if (!g_IsNT)
516     return ::GetFileAttributes(fs2fas(path));
517   else
518   #endif
519   {
520     IF_USE_MAIN_PATH
521     {
522       DWORD dw = ::GetFileAttributesW(fs2us(path));
523       if (dw != INVALID_FILE_ATTRIBUTES)
524         return dw;
525     }
526     #ifdef Z7_LONG_PATH
527     if (USE_SUPER_PATH)
528     {
529       UString superPath;
530       if (GetSuperPath(path, superPath, USE_MAIN_PATH))
531         return ::GetFileAttributesW(superPath);
532     }
533     #endif
534     return INVALID_FILE_ATTRIBUTES;
535   }
536 }
537 
538 /* if path is "c:" or "c::" then CFileInfo::Find() returns name of current folder for that disk
539    so instead of absolute path we have relative path in Name. That is not good in some calls */
540 
541 /* In CFileInfo::Find() we want to support same names for alt streams as in CreateFile(). */
542 
543 /* CFileInfo::Find()
544 We alow the following paths (as FindFirstFile):
545   C:\folder
546   c:                      - if current dir is NOT ROOT ( c:\folder )
547 
548 also we support paths that are not supported by FindFirstFile:
549   \
550   \\.\c:
551   c:\                     - Name will be without tail slash ( c: )
552   \\?\c:\                 - Name will be without tail slash ( c: )
553   \\Server\Share
554   \\?\UNC\Server\Share
555 
556   c:\folder:stream  - Name = folder:stream
557   c:\:stream        - Name = :stream
558   c::stream         - Name = c::stream
559 */
560 
Find(CFSTR path,bool followLink)561 bool CFileInfo::Find(CFSTR path, bool followLink)
562 {
563   #ifdef Z7_DEVICE_FILE
564 
565   if (IS_PATH_SEPAR(path[0]) &&
566       IS_PATH_SEPAR(path[1]) &&
567       path[2] == '.' &&
568       path[3] == 0)
569   {
570     // 22.00 : it's virtual directory for devices
571     // IsDevice = true;
572     ClearBase();
573     Name = path + 2;
574     Attrib = FILE_ATTRIBUTE_DIRECTORY;
575     return true;
576   }
577 
578   if (IsDevicePath(path))
579   {
580     ClearBase();
581     Name = path + 4;
582     IsDevice = true;
583 
584     if (NName::IsDrivePath2(path + 4) && path[6] == 0)
585     {
586       FChar drive[4] = { path[4], ':', '\\', 0 };
587       UInt64 clusterSize, totalSize, freeSize;
588       if (NSystem::MyGetDiskFreeSpace(drive, clusterSize, totalSize, freeSize))
589       {
590         Size = totalSize;
591         return true;
592       }
593     }
594 
595     NIO::CInFile inFile;
596     // ::OutputDebugStringW(path);
597     if (!inFile.Open(path))
598       return false;
599     // ::OutputDebugStringW(L"---");
600     if (inFile.SizeDefined)
601       Size = inFile.Size;
602     return true;
603   }
604   #endif
605 
606   #if defined(_WIN32) && !defined(UNDER_CE)
607 
608   const int colonPos = FindAltStreamColon(path);
609   if (colonPos >= 0 && path[(unsigned)colonPos + 1] != 0)
610   {
611     UString streamName = fs2us(path + (unsigned)colonPos);
612     FString filePath (path);
613     filePath.DeleteFrom((unsigned)colonPos);
614     /* we allow both cases:
615       name:stream
616       name:stream:$DATA
617     */
618     const unsigned kPostfixSize = 6;
619     if (streamName.Len() <= kPostfixSize
620         || !StringsAreEqualNoCase_Ascii(streamName.RightPtr(kPostfixSize), ":$DATA"))
621       streamName += ":$DATA";
622 
623     bool isOk = true;
624 
625     if (IsDrivePath2(filePath) &&
626         (colonPos == 2 || (colonPos == 3 && filePath[2] == '\\')))
627     {
628       // FindFirstFile doesn't work for "c:\" and for "c:" (if current dir is ROOT)
629       ClearBase();
630       Name.Empty();
631       if (colonPos == 2)
632         Name = filePath;
633     }
634     else
635       isOk = Find(filePath, followLink); // check it (followLink)
636 
637     if (isOk)
638     {
639       Attrib &= ~(DWORD)(FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT);
640       Size = 0;
641       CStreamEnumerator enumerator(filePath);
642       for (;;)
643       {
644         CStreamInfo si;
645         bool found;
646         if (!enumerator.Next(si, found))
647           return false;
648         if (!found)
649         {
650           ::SetLastError(ERROR_FILE_NOT_FOUND);
651           return false;
652         }
653         if (si.Name.IsEqualTo_NoCase(streamName))
654         {
655           // we delete postfix, if alt stream name is not "::$DATA"
656           if (si.Name.Len() > kPostfixSize + 1)
657             si.Name.DeleteFrom(si.Name.Len() - kPostfixSize);
658           Name += us2fs(si.Name);
659           Size = si.Size;
660           IsAltStream = true;
661           return true;
662         }
663       }
664     }
665   }
666 
667   #endif
668 
669   CFindFile finder;
670 
671   #if defined(_WIN32) && !defined(UNDER_CE)
672   {
673     /*
674     DWORD lastError = GetLastError();
675     if (lastError == ERROR_FILE_NOT_FOUND
676         || lastError == ERROR_BAD_NETPATH  // XP64: "\\Server\Share"
677         || lastError == ERROR_BAD_NET_NAME // Win7: "\\Server\Share"
678         || lastError == ERROR_INVALID_NAME // XP64: "\\?\UNC\Server\Share"
679         || lastError == ERROR_BAD_PATHNAME // Win7: "\\?\UNC\Server\Share"
680         )
681     */
682 
683     unsigned rootSize = 0;
684     if (IsSuperPath(path))
685       rootSize = kSuperPathPrefixSize;
686 
687     if (NName::IsDrivePath(path + rootSize) && path[rootSize + 3] == 0)
688     {
689       DWORD attrib = GetFileAttrib(path);
690       if (attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_DIRECTORY) != 0)
691       {
692         ClearBase();
693         Attrib = attrib;
694         Name = path + rootSize;
695         Name.DeleteFrom(2);
696         if (!Fill_From_ByHandleFileInfo(path))
697         {
698         }
699         return true;
700       }
701     }
702     else if (IS_PATH_SEPAR(path[0]))
703     {
704       if (path[1] == 0)
705       {
706         DWORD attrib = GetFileAttrib(path);
707         if (attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_DIRECTORY) != 0)
708         {
709           ClearBase();
710           Name.Empty();
711           Attrib = attrib;
712           return true;
713         }
714       }
715       else
716       {
717         const unsigned prefixSize = GetNetworkServerPrefixSize(path);
718         if (prefixSize > 0 && path[prefixSize] != 0)
719         {
720           if (NName::FindSepar(path + prefixSize) < 0)
721           {
722             if (Fill_From_ByHandleFileInfo(path))
723             {
724               Name = path + prefixSize;
725               return true;
726             }
727 
728             FString s (path);
729             s.Add_PathSepar();
730             s.Add_Char('*'); // CHAR_ANY_MASK
731             bool isOK = false;
732             if (finder.FindFirst(s, *this))
733             {
734               if (Name == FTEXT("."))
735               {
736                 Name = path + prefixSize;
737                 return true;
738               }
739               isOK = true;
740               /* if "\\server\share" maps to root folder "d:\", there is no "." item.
741                  But it's possible that there are another items */
742             }
743             {
744               const DWORD attrib = GetFileAttrib(path);
745               if (isOK || (attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_DIRECTORY) != 0))
746               {
747                 ClearBase();
748                 if (attrib != INVALID_FILE_ATTRIBUTES)
749                   Attrib = attrib;
750                 else
751                   SetAsDir();
752                 Name = path + prefixSize;
753                 return true;
754               }
755             }
756             // ::SetLastError(lastError);
757           }
758         }
759       }
760     }
761   }
762   #endif
763 
764   bool res = finder.FindFirst(path, *this);
765   if (!followLink
766       || !res
767       || !HasReparsePoint())
768     return res;
769 
770   // return FollowReparse(path, IsDir());
771   return Fill_From_ByHandleFileInfo(path);
772 }
773 
Fill_From_ByHandleFileInfo(CFSTR path)774 bool CFileInfoBase::Fill_From_ByHandleFileInfo(CFSTR path)
775 {
776   BY_HANDLE_FILE_INFORMATION info;
777   if (!NIO::CFileBase::GetFileInformation(path, &info))
778     return false;
779   {
780     Size = (((UInt64)info.nFileSizeHigh) << 32) + info.nFileSizeLow;
781     CTime = info.ftCreationTime;
782     ATime = info.ftLastAccessTime;
783     MTime = info.ftLastWriteTime;
784     Attrib = info.dwFileAttributes;
785     return true;
786   }
787 }
788 
789 /*
790 bool CFileInfo::FollowReparse(CFSTR path, bool isDir)
791 {
792   if (isDir)
793   {
794     FString prefix = path;
795     prefix.Add_PathSepar();
796 
797     // "folder/." refers to folder itself. So we can't use that path
798     // we must use enumerator and search "." item
799     CEnumerator enumerator;
800     enumerator.SetDirPrefix(prefix);
801     for (;;)
802     {
803       CFileInfo fi;
804       if (!enumerator.NextAny(fi))
805         break;
806       if (fi.Name.IsEqualTo_Ascii_NoCase("."))
807       {
808         // we can copy preperies;
809         CTime = fi.CTime;
810         ATime = fi.ATime;
811         MTime = fi.MTime;
812         Attrib = fi.Attrib;
813         Size = fi.Size;
814         return true;
815       }
816       break;
817     }
818     // LastError(lastError);
819     return false;
820   }
821 
822   {
823     NIO::CInFile inFile;
824     if (inFile.Open(path))
825     {
826       BY_HANDLE_FILE_INFORMATION info;
827       if (inFile.GetFileInformation(&info))
828       {
829         ClearBase();
830         Size = (((UInt64)info.nFileSizeHigh) << 32) + info.nFileSizeLow;
831         CTime = info.ftCreationTime;
832         ATime = info.ftLastAccessTime;
833         MTime = info.ftLastWriteTime;
834         Attrib = info.dwFileAttributes;
835         return true;
836       }
837     }
838     return false;
839   }
840 }
841 */
842 
DoesFileExist_Raw(CFSTR name)843 bool DoesFileExist_Raw(CFSTR name)
844 {
845   CFileInfo fi;
846   return fi.Find(name) && !fi.IsDir();
847 }
848 
DoesFileExist_FollowLink(CFSTR name)849 bool DoesFileExist_FollowLink(CFSTR name)
850 {
851   CFileInfo fi;
852   return fi.Find_FollowLink(name) && !fi.IsDir();
853 }
854 
DoesDirExist(CFSTR name,bool followLink)855 bool DoesDirExist(CFSTR name, bool followLink)
856 {
857   CFileInfo fi;
858   return fi.Find(name, followLink) && fi.IsDir();
859 }
860 
DoesFileOrDirExist(CFSTR name)861 bool DoesFileOrDirExist(CFSTR name)
862 {
863   CFileInfo fi;
864   return fi.Find(name);
865 }
866 
867 
SetDirPrefix(const FString & dirPrefix)868 void CEnumerator::SetDirPrefix(const FString &dirPrefix)
869 {
870   _wildcard = dirPrefix;
871   _wildcard.Add_Char('*');
872 }
873 
NextAny(CFileInfo & fi)874 bool CEnumerator::NextAny(CFileInfo &fi)
875 {
876   if (_findFile.IsHandleAllocated())
877     return _findFile.FindNext(fi);
878   else
879     return _findFile.FindFirst(_wildcard, fi);
880 }
881 
Next(CFileInfo & fi)882 bool CEnumerator::Next(CFileInfo &fi)
883 {
884   for (;;)
885   {
886     if (!NextAny(fi))
887       return false;
888     if (!fi.IsDots())
889       return true;
890   }
891 }
892 
Next(CFileInfo & fi,bool & found)893 bool CEnumerator::Next(CFileInfo &fi, bool &found)
894 {
895   /*
896   for (;;)
897   {
898     if (!NextAny(fi))
899       break;
900     if (!fi.IsDots())
901     {
902       found = true;
903       return true;
904     }
905   }
906   */
907 
908   if (Next(fi))
909   {
910     found = true;
911     return true;
912   }
913 
914   found = false;
915   DWORD lastError = ::GetLastError();
916   if (_findFile.IsHandleAllocated())
917     return (lastError == ERROR_NO_MORE_FILES);
918   // we support the case for empty root folder: FindFirstFile("c:\\*") returns ERROR_FILE_NOT_FOUND
919   if (lastError == ERROR_FILE_NOT_FOUND)
920     return true;
921   if (lastError == ERROR_ACCESS_DENIED)
922   {
923     // here we show inaccessible root system folder as empty folder to eliminate redundant user warnings
924     const char *s = "System Volume Information" STRING_PATH_SEPARATOR "*";
925     const int len = (int)strlen(s);
926     const int delta = (int)_wildcard.Len() - len;
927     if (delta == 0 || (delta > 0 && IS_PATH_SEPAR(_wildcard[(unsigned)delta - 1])))
928       if (StringsAreEqual_Ascii(_wildcard.Ptr((unsigned)delta), s))
929         return true;
930   }
931   return false;
932 }
933 
934 
935 ////////////////////////////////
936 // CFindChangeNotification
937 // FindFirstChangeNotification can return 0. MSDN doesn't tell about it.
938 
Close()939 bool CFindChangeNotification::Close() throw()
940 {
941   if (!IsHandleAllocated())
942     return true;
943   if (!::FindCloseChangeNotification(_handle))
944     return false;
945   _handle = INVALID_HANDLE_VALUE;
946   return true;
947 }
948 
FindFirst(CFSTR path,bool watchSubtree,DWORD notifyFilter)949 HANDLE CFindChangeNotification::FindFirst(CFSTR path, bool watchSubtree, DWORD notifyFilter)
950 {
951   #ifndef _UNICODE
952   if (!g_IsNT)
953     _handle = ::FindFirstChangeNotification(fs2fas(path), BoolToBOOL(watchSubtree), notifyFilter);
954   else
955   #endif
956   {
957     IF_USE_MAIN_PATH
958     _handle = ::FindFirstChangeNotificationW(fs2us(path), BoolToBOOL(watchSubtree), notifyFilter);
959     #ifdef Z7_LONG_PATH
960     if (!IsHandleAllocated())
961     {
962       UString superPath;
963       if (GetSuperPath(path, superPath, USE_MAIN_PATH))
964         _handle = ::FindFirstChangeNotificationW(superPath, BoolToBOOL(watchSubtree), notifyFilter);
965     }
966     #endif
967   }
968   return _handle;
969 }
970 
971 #ifndef UNDER_CE
972 
MyGetLogicalDriveStrings(CObjectVector<FString> & driveStrings)973 bool MyGetLogicalDriveStrings(CObjectVector<FString> &driveStrings)
974 {
975   driveStrings.Clear();
976   #ifndef _UNICODE
977   if (!g_IsNT)
978   {
979     driveStrings.Clear();
980     UINT32 size = GetLogicalDriveStrings(0, NULL);
981     if (size == 0)
982       return false;
983     CObjArray<char> buf(size);
984     UINT32 newSize = GetLogicalDriveStrings(size, buf);
985     if (newSize == 0 || newSize > size)
986       return false;
987     AString s;
988     UINT32 prev = 0;
989     for (UINT32 i = 0; i < newSize; i++)
990     {
991       if (buf[i] == 0)
992       {
993         s = buf + prev;
994         prev = i + 1;
995         driveStrings.Add(fas2fs(s));
996       }
997     }
998     return prev == newSize;
999   }
1000   else
1001   #endif
1002   {
1003     UINT32 size = GetLogicalDriveStringsW(0, NULL);
1004     if (size == 0)
1005       return false;
1006     CObjArray<wchar_t> buf(size);
1007     UINT32 newSize = GetLogicalDriveStringsW(size, buf);
1008     if (newSize == 0 || newSize > size)
1009       return false;
1010     UString s;
1011     UINT32 prev = 0;
1012     for (UINT32 i = 0; i < newSize; i++)
1013     {
1014       if (buf[i] == 0)
1015       {
1016         s = buf + prev;
1017         prev = i + 1;
1018         driveStrings.Add(us2fs(s));
1019       }
1020     }
1021     return prev == newSize;
1022   }
1023 }
1024 
1025 #endif // UNDER_CE
1026 
1027 
1028 
1029 #else // _WIN32
1030 
1031 // ---------- POSIX ----------
1032 
MY_lstat(CFSTR path,struct stat * st,bool followLink)1033 static int MY_lstat(CFSTR path, struct stat *st, bool followLink)
1034 {
1035   memset(st, 0, sizeof(*st));
1036   int res;
1037   // #ifdef ENV_HAVE_LSTAT
1038   if (/* global_use_lstat && */ !followLink)
1039   {
1040     // printf("\nlstat\n");
1041     res = lstat(path, st);
1042   }
1043   else
1044   // #endif
1045   {
1046     // printf("\nstat\n");
1047     res = stat(path, st);
1048   }
1049 #if 0
1050 #if defined(__clang__) && __clang_major__ >= 14
1051   #pragma GCC diagnostic ignored "-Wc++98-compat-pedantic"
1052 #endif
1053 
1054   printf("\n st_dev = %lld", (long long)(st->st_dev));
1055   printf("\n st_ino = %lld", (long long)(st->st_ino));
1056   printf("\n st_mode = %llx", (long long)(st->st_mode));
1057   printf("\n st_nlink = %lld", (long long)(st->st_nlink));
1058   printf("\n st_uid = %lld", (long long)(st->st_uid));
1059   printf("\n st_gid = %lld", (long long)(st->st_gid));
1060   printf("\n st_size = %lld", (long long)(st->st_size));
1061   printf("\n st_blksize = %lld", (long long)(st->st_blksize));
1062   printf("\n st_blocks = %lld", (long long)(st->st_blocks));
1063   printf("\n st_ctim = %lld", (long long)(ST_CTIME((*st)).tv_sec));
1064   printf("\n st_mtim = %lld", (long long)(ST_MTIME((*st)).tv_sec));
1065   printf("\n st_atim = %lld", (long long)(ST_ATIME((*st)).tv_sec));
1066      printf(S_ISFIFO(st->st_mode) ? "\n FIFO" : "\n NO FIFO");
1067   printf("\n");
1068 #endif
1069 
1070   return res;
1071 }
1072 
1073 
Get_Name_from_Path(CFSTR path)1074 static const char *Get_Name_from_Path(CFSTR path) throw()
1075 {
1076   size_t len = strlen(path);
1077   if (len == 0)
1078     return path;
1079   const char *p = path + len - 1;
1080   {
1081     if (p == path)
1082       return path;
1083     p--;
1084   }
1085   for (;;)
1086   {
1087     char c = *p;
1088     if (IS_PATH_SEPAR(c))
1089       return p + 1;
1090     if (p == path)
1091       return path;
1092     p--;
1093   }
1094 }
1095 
1096 
Get_WinAttribPosix_From_PosixMode(UInt32 mode)1097 UInt32 Get_WinAttribPosix_From_PosixMode(UInt32 mode)
1098 {
1099   UInt32 attrib = S_ISDIR(mode) ?
1100       FILE_ATTRIBUTE_DIRECTORY :
1101       FILE_ATTRIBUTE_ARCHIVE;
1102   if ((mode & 0222) == 0) // S_IWUSR in p7zip
1103     attrib |= FILE_ATTRIBUTE_READONLY;
1104   return attrib | FILE_ATTRIBUTE_UNIX_EXTENSION | ((mode & 0xFFFF) << 16);
1105 }
1106 
1107 /*
1108 UInt32 Get_WinAttrib_From_stat(const struct stat &st)
1109 {
1110   UInt32 attrib = S_ISDIR(st.st_mode) ?
1111     FILE_ATTRIBUTE_DIRECTORY :
1112     FILE_ATTRIBUTE_ARCHIVE;
1113 
1114   if ((st.st_mode & 0222) == 0) // check it !!!
1115     attrib |= FILE_ATTRIBUTE_READONLY;
1116 
1117   attrib |= FILE_ATTRIBUTE_UNIX_EXTENSION + ((st.st_mode & 0xFFFF) << 16);
1118   return attrib;
1119 }
1120 */
1121 
SetFrom_stat(const struct stat & st)1122 void CFileInfoBase::SetFrom_stat(const struct stat &st)
1123 {
1124   // IsDevice = false;
1125 
1126   if (S_ISDIR(st.st_mode))
1127   {
1128     Size = 0;
1129   }
1130   else
1131   {
1132     Size = (UInt64)st.st_size; // for a symbolic link, size = size of filename
1133   }
1134 
1135   // Attrib = Get_WinAttribPosix_From_PosixMode(st.st_mode);
1136 
1137   // NTime::UnixTimeToFileTime(st.st_ctime, CTime);
1138   // NTime::UnixTimeToFileTime(st.st_mtime, MTime);
1139   // NTime::UnixTimeToFileTime(st.st_atime, ATime);
1140   #ifdef __APPLE__
1141   // #ifdef _DARWIN_FEATURE_64_BIT_INODE
1142   /*
1143     here we can use birthtime instead of st_ctimespec.
1144     but we use st_ctimespec for compatibility with previous versions and p7zip.
1145     st_birthtimespec in OSX
1146     st_birthtim : at FreeBSD, NetBSD
1147   */
1148   // timespec_To_FILETIME(st.st_birthtimespec, CTime);
1149   // #else
1150   // timespec_To_FILETIME(st.st_ctimespec, CTime);
1151   // #endif
1152   // timespec_To_FILETIME(st.st_mtimespec, MTime);
1153   // timespec_To_FILETIME(st.st_atimespec, ATime);
1154   CTime = st.st_ctimespec;
1155   MTime = st.st_mtimespec;
1156   ATime = st.st_atimespec;
1157 
1158   #else
1159   // timespec_To_FILETIME(st.st_ctim, CTime, &CTime_ns100);
1160   // timespec_To_FILETIME(st.st_mtim, MTime, &MTime_ns100);
1161   // timespec_To_FILETIME(st.st_atim, ATime, &ATime_ns100);
1162   CTime = st.st_ctim;
1163   MTime = st.st_mtim;
1164   ATime = st.st_atim;
1165 
1166   #endif
1167 
1168   dev = st.st_dev;
1169   ino = st.st_ino;
1170   mode = st.st_mode;
1171   nlink = st.st_nlink;
1172   uid = st.st_uid;
1173   gid = st.st_gid;
1174   rdev = st.st_rdev;
1175 
1176   /*
1177   printf("\n sizeof timespec = %d", (int)sizeof(timespec));
1178   printf("\n sizeof st_rdev = %d", (int)sizeof(rdev));
1179   printf("\n sizeof st_ino = %d", (int)sizeof(ino));
1180   printf("\n sizeof mode_t = %d", (int)sizeof(mode_t));
1181   printf("\n sizeof nlink_t = %d", (int)sizeof(nlink_t));
1182   printf("\n sizeof uid_t = %d", (int)sizeof(uid_t));
1183   printf("\n");
1184   */
1185   /*
1186   printf("\n st_rdev = %llx", (long long)rdev);
1187   printf("\n st_dev  = %llx", (long long)dev);
1188   printf("\n dev  : major = %5x minor = %5x", (unsigned)major(dev), (unsigned)minor(dev));
1189   printf("\n st_ino = %lld", (long long)(ino));
1190   printf("\n rdev : major = %5x minor = %5x", (unsigned)major(rdev), (unsigned)minor(rdev));
1191   printf("\n size = %lld \n", (long long)(Size));
1192   printf("\n");
1193   */
1194 }
1195 
1196 /*
1197 int Uid_To_Uname(uid_t uid, AString &name)
1198 {
1199   name.Empty();
1200   struct passwd *passwd;
1201 
1202   if (uid != 0 && uid == cached_no_such_uid)
1203     {
1204       *uname = xstrdup ("");
1205       return;
1206     }
1207 
1208   if (!cached_uname || uid != cached_uid)
1209     {
1210       passwd = getpwuid (uid);
1211       if (passwd)
1212   {
1213     cached_uid = uid;
1214     assign_string (&cached_uname, passwd->pw_name);
1215   }
1216       else
1217   {
1218     cached_no_such_uid = uid;
1219     *uname = xstrdup ("");
1220     return;
1221   }
1222     }
1223   *uname = xstrdup (cached_uname);
1224 }
1225 */
1226 
Find_DontFill_Name(CFSTR path,bool followLink)1227 bool CFileInfo::Find_DontFill_Name(CFSTR path, bool followLink)
1228 {
1229   struct stat st;
1230   if (MY_lstat(path, &st, followLink) != 0)
1231     return false;
1232   // printf("\nFind_DontFill_Name : name=%s\n", path);
1233   SetFrom_stat(st);
1234   return true;
1235 }
1236 
1237 
Find(CFSTR path,bool followLink)1238 bool CFileInfo::Find(CFSTR path, bool followLink)
1239 {
1240   // printf("\nCEnumerator::Find() name = %s\n", path);
1241   if (!Find_DontFill_Name(path, followLink))
1242     return false;
1243 
1244   // printf("\nOK\n");
1245 
1246   Name = Get_Name_from_Path(path);
1247   if (!Name.IsEmpty())
1248   {
1249     char c = Name.Back();
1250     if (IS_PATH_SEPAR(c))
1251       Name.DeleteBack();
1252   }
1253   return true;
1254 }
1255 
1256 
DoesFileExist_Raw(CFSTR name)1257 bool DoesFileExist_Raw(CFSTR name)
1258 {
1259   // FIXME for symbolic links.
1260   struct stat st;
1261   if (MY_lstat(name, &st, false) != 0)
1262     return false;
1263   return !S_ISDIR(st.st_mode);
1264 }
1265 
DoesFileExist_FollowLink(CFSTR name)1266 bool DoesFileExist_FollowLink(CFSTR name)
1267 {
1268   // FIXME for symbolic links.
1269   struct stat st;
1270   if (MY_lstat(name, &st, true) != 0)
1271     return false;
1272   return !S_ISDIR(st.st_mode);
1273 }
1274 
DoesDirExist(CFSTR name,bool followLink)1275 bool DoesDirExist(CFSTR name, bool followLink)
1276 {
1277   struct stat st;
1278   if (MY_lstat(name, &st, followLink) != 0)
1279     return false;
1280   return S_ISDIR(st.st_mode);
1281 }
1282 
DoesFileOrDirExist(CFSTR name)1283 bool DoesFileOrDirExist(CFSTR name)
1284 {
1285   struct stat st;
1286   if (MY_lstat(name, &st, false) != 0)
1287     return false;
1288   return true;
1289 }
1290 
1291 
~CEnumerator()1292 CEnumerator::~CEnumerator()
1293 {
1294   if (_dir)
1295     closedir(_dir);
1296 }
1297 
SetDirPrefix(const FString & dirPrefix)1298 void CEnumerator::SetDirPrefix(const FString &dirPrefix)
1299 {
1300   _wildcard = dirPrefix;
1301 }
1302 
IsDots() const1303 bool CDirEntry::IsDots() const throw()
1304 {
1305   /* some systems (like CentOS 7.x on XFS) have (Type == DT_UNKNOWN)
1306      we can call fstatat() for that case, but we use only (Name) check here */
1307 
1308 #if !defined(_AIX) && !defined(__sun)
1309   if (Type != DT_DIR && Type != DT_UNKNOWN)
1310     return false;
1311 #endif
1312 
1313   return Name.Len() != 0
1314       && Name.Len() <= 2
1315       && Name[0] == '.'
1316       && (Name.Len() == 1 || Name[1] == '.');
1317 }
1318 
1319 
NextAny(CDirEntry & fi,bool & found)1320 bool CEnumerator::NextAny(CDirEntry &fi, bool &found)
1321 {
1322   found = false;
1323 
1324   if (!_dir)
1325   {
1326     const char *w = "./";
1327     if (!_wildcard.IsEmpty())
1328       w = _wildcard.Ptr();
1329     _dir = ::opendir((const char *)w);
1330     if (_dir == NULL)
1331       return false;
1332   }
1333 
1334   // To distinguish end of stream from an error, we must set errno to zero before readdir()
1335   errno = 0;
1336 
1337   struct dirent *de = readdir(_dir);
1338   if (!de)
1339   {
1340     if (errno == 0)
1341       return true; // it's end of stream, and we report it with (found = false)
1342     // it's real error
1343     return false;
1344   }
1345 
1346   fi.iNode = de->d_ino;
1347 
1348 #if !defined(_AIX) && !defined(__sun)
1349   fi.Type = de->d_type;
1350   /* some systems (like CentOS 7.x on XFS) have (Type == DT_UNKNOWN)
1351      we can set (Type) from fstatat() in that case.
1352      But (Type) is not too important. So we don't set it here with slow fstatat() */
1353   /*
1354   // fi.Type = DT_UNKNOWN; // for debug
1355   if (fi.Type == DT_UNKNOWN)
1356   {
1357     struct stat st;
1358     if (fstatat(dirfd(_dir), de->d_name, &st, AT_SYMLINK_NOFOLLOW) == 0)
1359       if (S_ISDIR(st.st_mode))
1360         fi.Type = DT_DIR;
1361   }
1362   */
1363 #endif
1364 
1365   /*
1366   if (de->d_type == DT_DIR)
1367     fi.Attrib = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_UNIX_EXTENSION | ((UInt32)(S_IFDIR) << 16);
1368   else if (de->d_type < 16)
1369     fi.Attrib = FILE_ATTRIBUTE_UNIX_EXTENSION | ((UInt32)(de->d_type) << (16 + 12));
1370   */
1371   fi.Name = de->d_name;
1372 
1373   /*
1374   printf("\nCEnumerator::NextAny; len = %d %s \n", (int)fi.Name.Len(), fi.Name.Ptr());
1375   for (unsigned i = 0; i < fi.Name.Len(); i++)
1376     printf (" %02x", (unsigned)(Byte)de->d_name[i]);
1377   printf("\n");
1378   */
1379 
1380   found = true;
1381   return true;
1382 }
1383 
1384 
Next(CDirEntry & fi,bool & found)1385 bool CEnumerator::Next(CDirEntry &fi, bool &found)
1386 {
1387   // printf("\nCEnumerator::Next()\n");
1388   // PrintName("Next", "");
1389   for (;;)
1390   {
1391     if (!NextAny(fi, found))
1392       return false;
1393     if (!found)
1394       return true;
1395     if (!fi.IsDots())
1396     {
1397       /*
1398       if (!NeedFullStat)
1399         return true;
1400       // we silently skip error file here - it can be wrong link item
1401       if (fi.Find_DontFill_Name(path))
1402         return true;
1403       */
1404       return true;
1405     }
1406   }
1407 }
1408 
1409 /*
1410 bool CEnumerator::Next(CDirEntry &fileInfo, bool &found)
1411 {
1412   bool found;
1413   if (!Next(fi, found))
1414     return false;
1415   return found;
1416 }
1417 */
1418 
Fill_FileInfo(const CDirEntry & de,CFileInfo & fileInfo,bool followLink) const1419 bool CEnumerator::Fill_FileInfo(const CDirEntry &de, CFileInfo &fileInfo, bool followLink) const
1420 {
1421   // printf("\nCEnumerator::Fill_FileInfo()\n");
1422   struct stat st;
1423   // probably it's OK to use fstatat() even if it changes file position dirfd(_dir)
1424   int res = fstatat(dirfd(_dir), de.Name, &st, followLink ? 0 : AT_SYMLINK_NOFOLLOW);
1425   // if fstatat() is not supported, we can use stat() / lstat()
1426 
1427   /*
1428   const FString path = _wildcard + s;
1429   int res = MY_lstat(path, &st, followLink);
1430   */
1431 
1432   if (res != 0)
1433     return false;
1434   // printf("\nname=%s\n", de.Name.Ptr());
1435   fileInfo.SetFrom_stat(st);
1436   fileInfo.Name = de.Name;
1437   return true;
1438 }
1439 
1440 #endif // _WIN32
1441 
1442 }}}
1443