langpackedit v0.13 -- from 8f9f0878
[wdl.git] / WDL / dirscan.h
blob81c055605afaadb3269b32d9ac338abd8b63dce9
1 /*
2 WDL - dirscan.h
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
28 well.
34 #ifndef _WDL_DIRSCAN_H_
35 #define _WDL_DIRSCAN_H_
37 #include "wdlstring.h"
39 #ifndef _WIN32
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <dirent.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];
46 #endif
48 class WDL_DirScan
50 public:
51 WDL_DirScan() :
52 #ifdef _WIN32
53 m_h(INVALID_HANDLE_VALUE)
54 #ifndef WDL_NO_SUPPORT_UTF8
55 , m_wcmode(false)
56 #endif
57 #else
58 m_h(NULL), m_ent(NULL)
59 #endif
63 ~WDL_DirScan()
65 Close();
68 int First(const char *dirname
69 #ifdef _WIN32
70 , int isExactSpec=0
71 #endif
72 ) // returns 0 if success
74 WDL_FastString scanstr(dirname);
75 const int l = scanstr.GetLength();
76 if (l < 1) return -1;
78 #ifdef _WIN32
79 if (!isExactSpec)
81 if (dirname[l-1] == '\\' || dirname[l-1] == '/') scanstr.SetLen(l-1);
82 m_leading_path = scanstr;
83 scanstr.Append("\\*");
85 else
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);
95 #else
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 /
99 #endif
101 Close();
102 #ifdef _WIN32
103 #ifndef WDL_NO_SUPPORT_UTF8
104 m_h=INVALID_HANDLE_VALUE;
105 #ifdef WDL_SUPPORT_WIN9X
106 m_wcmode = GetVersion()< 0x80000000;
107 #else
108 m_wcmode = true;
109 #endif
111 if (m_wcmode)
113 int reqbuf = MultiByteToWideChar(CP_UTF8,MB_ERR_INVALID_CHARS,scanstr.Get(),-1,NULL,0);
114 if (reqbuf > 1000)
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);
124 else
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)
138 #endif
139 m_h=FindFirstFileA(scanstr.Get(),(WIN32_FIND_DATAA*)&m_fd);
140 return (m_h == INVALID_HANDLE_VALUE);
141 #else
142 m_ent=0;
143 m_h=opendir(scanstr.Get());
144 return !m_h || Next();
145 #endif
147 int Next() // returns 0 on success
149 #ifdef _WIN32
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);
153 #endif
154 return !FindNextFileA(m_h,(WIN32_FIND_DATAA*)&m_fd);
155 #else
156 if (!m_h) return -1;
157 return !(m_ent=readdir(m_h));
158 #endif
160 void Close()
162 #ifdef _WIN32
163 if (m_h != INVALID_HANDLE_VALUE) FindClose(m_h);
164 m_h=INVALID_HANDLE_VALUE;
165 #else
166 if (m_h) closedir(m_h);
167 m_h=0; m_ent=0;
168 #endif
171 #ifdef _WIN32
172 const char *GetCurrentFN()
174 #ifndef WDL_NO_SUPPORT_UTF8
175 if (m_wcmode)
177 if (!WideCharToMultiByte(CP_UTF8,0,m_fd.cFileName,-1,m_tmpbuf,sizeof(m_tmpbuf),NULL,NULL))
178 m_tmpbuf[0]=0;
179 return m_tmpbuf;
181 #endif
182 return ((WIN32_FIND_DATAA *)&m_fd)->cFileName;
184 #else
185 const char *GetCurrentFN() const { return m_ent?m_ent->d_name : ""; }
186 #endif
187 template<class T> void GetCurrentFullFN(T *str)
189 str->Set(m_leading_path.Get());
190 #ifdef _WIN32
191 str->Append("\\");
192 #else
193 str->Append("/");
194 #endif
195 str->Append(GetCurrentFN());
197 int GetCurrentIsDirectory() const // returns 1 if dir, 2 if symlink to dir, 4 if possibly-recursive symlink to dir
199 #ifdef _WIN32
200 return !!(m_fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
201 #else
202 char tmp[2048];
203 if (m_ent) switch (m_ent->d_type)
205 case DT_DIR: return 1;
206 case DT_LNK:
208 snprintf(tmp,sizeof(tmp),"%s/%s",m_leading_path.Get(),m_ent->d_name);
209 char *rp = realpath(tmp,NULL);
210 if (!rp) return 0;
212 struct stat sb;
213 int ret = (!stat(rp,&sb) && (sb.st_mode & S_IFMT) == S_IFDIR) ? 2 : 0;
214 if (ret)
216 // treat symlinks of /path/to/foo -> /path from being resolved (avoiding obvious feedback loops)
217 const int rpl = (int) strlen(rp);
218 if (
219 #ifdef __APPLE__
220 !strnicmp(rp,m_leading_path.Get(),rpl)
221 #else
222 !strncmp(rp,m_leading_path.Get(),rpl)
223 #endif
224 && (m_leading_path.Get()[rpl] == '/' || m_leading_path.Get()[rpl] == 0)
225 ) ret = 4;
227 free(rp);
228 return ret;
230 case DT_UNKNOWN:
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; }
235 return 0;
238 return 0;
239 #endif
242 // these are somewhat windows specific calls, eh
243 #ifdef _WIN32
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)
253 char tmp[2048];
254 snprintf(tmp,sizeof(tmp),"%s/%s",m_leading_path.Get(),GetCurrentFN());
255 struct stat st={0,};
256 stat(tmp,&st);
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)
266 char tmp[2048];
267 snprintf(tmp,sizeof(tmp),"%s/%s",m_leading_path.Get(),GetCurrentFN());
268 struct stat st={0,};
269 stat(tmp,&st);
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)
278 char tmp[2048];
279 snprintf(tmp,sizeof(tmp),"%s/%s",m_leading_path.Get(),GetCurrentFN());
280 struct stat st={0,};
281 stat(tmp,&st);
283 if (HighWord) *HighWord = (DWORD)(st.st_size>>32);
284 return (DWORD)(st.st_size&0xffffffff);
287 #endif
289 private:
290 #ifdef _WIN32
292 #ifndef WDL_NO_SUPPORT_UTF8
293 bool m_wcmode;
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 ;)
296 #else
297 WIN32_FIND_DATAA m_fd;
298 #endif
299 HANDLE m_h;
300 #else
301 DIR *m_h;
302 struct dirent *m_ent;
303 #endif
304 WDL_FastString m_leading_path;
306 #ifdef _WIN32
307 static void correctlongpath(WCHAR *buf) // this also exists as wdl_utf8_correctlongpath
309 const WCHAR *insert;
310 WCHAR *wr;
311 int skip = 0;
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; }
315 else return;
317 wr = buf + wcslen(insert);
318 memmove(wr, buf + skip, (wcslen(buf+skip)+1)*2);
319 memmove(buf,insert,wcslen(insert)*2);
320 while (*wr)
322 if (*wr == '/') *wr = '\\';
323 wr++;
326 #endif
327 } WDL_FIXALIGN;
329 #endif