3 Copyright (C) 2005 and later Cockos Incorporated
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
25 This file provides the interface and implementation for WDL_DirScan, a simple
26 (and somewhat portable) directory reading class. On non-Win32 systems it wraps
27 opendir()/readdir()/etc. On Win32, it uses FindFirst*, and supports wildcards as
34 #ifndef _WDL_DIRSCAN_H_
35 #define _WDL_DIRSCAN_H_
37 #include "wdlstring.h"
40 #include <sys/types.h>
43 extern struct stat wdl_stat_chk
;
44 // if this fails on linux, use CFLAGS += -D_FILE_OFFSET_BITS=64
45 typedef char wdl_dirscan_assert_failed_stat_not_64
[sizeof(wdl_stat_chk
.st_size
)!=8 ? -1 : 1];
53 m_h(INVALID_HANDLE_VALUE
)
54 #ifndef WDL_NO_SUPPORT_UTF8
58 m_h(NULL
), m_ent(NULL
)
68 int First(const char *dirname
72 ) // returns 0 if success
74 WDL_FastString
scanstr(dirname
);
75 const int l
= scanstr
.GetLength();
81 if (dirname
[l
-1] == '\\' || dirname
[l
-1] == '/') scanstr
.SetLen(l
-1);
82 m_leading_path
= scanstr
;
83 scanstr
.Append("\\*");
87 m_leading_path
= scanstr
;
89 // remove trailing wildcards and directory separator from m_leading_path
90 const char *sp
= m_leading_path
.Get();
91 int idx
= m_leading_path
.GetLength() - 1;
92 while (idx
> 0 && sp
[idx
] != '/' && sp
[idx
] != '\\') idx
--;
93 if (idx
> 0) m_leading_path
.SetLen(idx
);
96 if (dirname
[l
-1] == '\\' || dirname
[l
-1] == '/') scanstr
.SetLen(l
-1);
97 m_leading_path
= scanstr
;
98 if (!scanstr
.GetLength()) scanstr
.Set("/"); // fix for scanning /
103 #ifndef WDL_NO_SUPPORT_UTF8
104 m_h
=INVALID_HANDLE_VALUE
;
105 #ifdef WDL_SUPPORT_WIN9X
106 m_wcmode
= GetVersion()< 0x80000000;
113 int reqbuf
= MultiByteToWideChar(CP_UTF8
,MB_ERR_INVALID_CHARS
,scanstr
.Get(),-1,NULL
,0);
116 WDL_TypedBuf
<WCHAR
> tmp
;
117 tmp
.Resize(reqbuf
+20);
118 if (MultiByteToWideChar(CP_UTF8
,MB_ERR_INVALID_CHARS
,scanstr
.Get(),-1,tmp
.Get(),tmp
.GetSize()-10))
120 correctlongpath(tmp
.Get());
121 m_h
=FindFirstFileW(tmp
.Get(),&m_fd
);
126 WCHAR wfilename
[1024];
127 if (MultiByteToWideChar(CP_UTF8
,MB_ERR_INVALID_CHARS
,scanstr
.Get(),-1,wfilename
,1024-10))
129 correctlongpath(wfilename
);
130 m_h
=FindFirstFileW(wfilename
,&m_fd
);
135 if (m_h
==INVALID_HANDLE_VALUE
) m_wcmode
=false;
137 if (m_h
==INVALID_HANDLE_VALUE
)
139 m_h
=FindFirstFileA(scanstr
.Get(),(WIN32_FIND_DATAA
*)&m_fd
);
140 return (m_h
== INVALID_HANDLE_VALUE
);
143 m_h
=opendir(scanstr
.Get());
144 return !m_h
|| Next();
147 int Next() // returns 0 on success
150 if (m_h
== INVALID_HANDLE_VALUE
) return -1;
151 #ifndef WDL_NO_SUPPORT_UTF8
152 if (m_wcmode
) return !FindNextFileW(m_h
,&m_fd
);
154 return !FindNextFileA(m_h
,(WIN32_FIND_DATAA
*)&m_fd
);
157 return !(m_ent
=readdir(m_h
));
163 if (m_h
!= INVALID_HANDLE_VALUE
) FindClose(m_h
);
164 m_h
=INVALID_HANDLE_VALUE
;
166 if (m_h
) closedir(m_h
);
172 const char *GetCurrentFN()
174 #ifndef WDL_NO_SUPPORT_UTF8
177 if (!WideCharToMultiByte(CP_UTF8
,0,m_fd
.cFileName
,-1,m_tmpbuf
,sizeof(m_tmpbuf
),NULL
,NULL
))
182 return ((WIN32_FIND_DATAA
*)&m_fd
)->cFileName
;
185 const char *GetCurrentFN() const { return m_ent
?m_ent
->d_name
: ""; }
187 template<class T
> void GetCurrentFullFN(T
*str
)
189 str
->Set(m_leading_path
.Get());
195 str
->Append(GetCurrentFN());
197 int GetCurrentIsDirectory() const // returns 1 if dir, 2 if symlink to dir, 4 if possibly-recursive symlink to dir
200 return !!(m_fd
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
);
203 if (m_ent
) switch (m_ent
->d_type
)
205 case DT_DIR
: return 1;
208 snprintf(tmp
,sizeof(tmp
),"%s/%s",m_leading_path
.Get(),m_ent
->d_name
);
209 char *rp
= realpath(tmp
,NULL
);
213 int ret
= (!stat(rp
,&sb
) && (sb
.st_mode
& S_IFMT
) == S_IFDIR
) ? 2 : 0;
216 // treat symlinks of /path/to/foo -> /path from being resolved (avoiding obvious feedback loops)
217 const int rpl
= (int) strlen(rp
);
220 !strnicmp(rp
,m_leading_path
.Get(),rpl
)
222 !strncmp(rp
,m_leading_path
.Get(),rpl
)
224 && (m_leading_path
.Get()[rpl
] == '/' || m_leading_path
.Get()[rpl
] == 0)
232 snprintf(tmp
,sizeof(tmp
),"%s/%s",m_leading_path
.Get(),m_ent
->d_name
);
233 DIR *d
= opendir(tmp
);
234 if (d
) { closedir(d
); return 1; }
242 // these are somewhat windows specific calls, eh
244 DWORD
GetCurrentFileSize(DWORD
*HighWord
=NULL
) const { if (HighWord
) *HighWord
= m_fd
.nFileSizeHigh
; return m_fd
.nFileSizeLow
; }
245 void GetCurrentLastWriteTime(FILETIME
*ft
) const { *ft
= m_fd
.ftLastWriteTime
; }
246 void GetCurrentLastAccessTime(FILETIME
*ft
) const { *ft
= m_fd
.ftLastAccessTime
; }
247 void GetCurrentCreationTime(FILETIME
*ft
) const { *ft
= m_fd
.ftCreationTime
; }
248 DWORD
GetFileAttributes() const { return m_fd
.dwFileAttributes
; }
249 #elif defined(_WDL_SWELL_H_)
251 void GetCurrentCreationTime(FILETIME
*ft
)
254 snprintf(tmp
,sizeof(tmp
),"%s/%s",m_leading_path
.Get(),GetCurrentFN());
257 unsigned long long a
=(unsigned long long)st
.st_ctime
; // seconds since january 1st, 1970
258 a
+=11644473600ull; // 1601->1970
259 a
*=10000000; // seconds to 1/10th microseconds (100 nanoseconds)
260 ft
->dwLowDateTime
=a
& 0xffffffff;
261 ft
->dwHighDateTime
=a
>>32;
264 void GetCurrentLastWriteTime(FILETIME
*ft
)
267 snprintf(tmp
,sizeof(tmp
),"%s/%s",m_leading_path
.Get(),GetCurrentFN());
270 unsigned long long a
=(unsigned long long)st
.st_mtime
; // seconds since january 1st, 1970
271 a
+=11644473600ull; // 1601->1970
272 a
*=10000000; // seconds to 1/10th microseconds (100 nanoseconds)
273 ft
->dwLowDateTime
=a
& 0xffffffff;
274 ft
->dwHighDateTime
=a
>>32;
276 DWORD
GetCurrentFileSize(DWORD
*HighWord
=NULL
)
279 snprintf(tmp
,sizeof(tmp
),"%s/%s",m_leading_path
.Get(),GetCurrentFN());
283 if (HighWord
) *HighWord
= (DWORD
)(st
.st_size
>>32);
284 return (DWORD
)(st
.st_size
&0xffffffff);
292 #ifndef WDL_NO_SUPPORT_UTF8
294 WIN32_FIND_DATAW m_fd
;
295 char m_tmpbuf
[MAX_PATH
*5]; // even if each byte gets encoded as 4 utf-8 bytes this should be plenty ;)
297 WIN32_FIND_DATAA m_fd
;
302 struct dirent
*m_ent
;
304 WDL_FastString m_leading_path
;
307 static void correctlongpath(WCHAR
*buf
) // this also exists as wdl_utf8_correctlongpath
312 if (!buf
|| !buf
[0] || wcslen(buf
) < 256) return;
313 if (buf
[1] == ':') insert
=L
"\\\\?\\";
314 else if (buf
[0] == '\\' && buf
[1] == '\\') { insert
= L
"\\\\?\\UNC\\"; skip
=2; }
317 wr
= buf
+ wcslen(insert
);
318 memmove(wr
, buf
+ skip
, (wcslen(buf
+skip
)+1)*2);
319 memmove(buf
,insert
,wcslen(insert
)*2);
322 if (*wr
== '/') *wr
= '\\';