1 // VFSTools.cpp - useful functions and misc stuff
2 // For conditions of distribution and use, see copyright notice in VFS.h
3
4 #include "VFSInternal.h"
5 #include "VFSTools.h"
6
7 #include <algorithm>
8 #include <ctype.h>
9
10 #if _WIN32
11 # define WIN32_LEAN_AND_MEAN
12 # include <windows.h>
13 # include <io.h>
14 #else
15 # include <dirent.h>
16 # include <unistd.h>
17 #endif
18
19 #include <sys/types.h>
20 #include <sys/stat.h>
21
22 VFS_NAMESPACE_START
23
24
25 #if !_WIN32
26 #ifdef DT_DIR
_IsFile(const char * path,dirent * dp)27 static bool _IsFile(const char *path, dirent *dp)
28 {
29 switch(dp->d_type)
30 {
31 case DT_DIR:
32 return false;
33 case DT_LNK:
34 {
35 std::string fullname = path;
36 fullname += '/';
37 fullname += dp->d_name;
38 struct stat statbuf;
39 if(stat(fullname.c_str(), &statbuf))
40 return false; // error
41 return !S_ISDIR(statbuf.st_mode);
42 }
43 // TODO: for now, we consider other file types as regular files
44 default:
45 ;
46 }
47 return true;
48 }
49
_IsDir(const char * path,dirent * dp)50 static bool _IsDir(const char *path, dirent *dp)
51 {
52 switch(dp->d_type)
53 {
54 case DT_DIR:
55 return true;
56 case DT_LNK:
57 {
58 std::string fullname = path;
59 fullname += '/';
60 fullname += dp->d_name;
61 struct stat statbuf;
62 if(stat(fullname.c_str(), &statbuf))
63 return false; // error
64 return S_ISDIR(statbuf.st_mode);
65 }
66 default:
67 ;
68 }
69 return false;
70 }
71
72 #else // No DT_DIR, assume plain POSIX
73
74 static bool _IsDir(const char *path, dirent *dp)
75 {
76 const int len1 = strlen(path);
77 const int len2 = strlen(dp->d_name);
78
79 char *pathname = (char*)alloca(len1 + 1 + len2 + 1 + 13);
80 strcpy (pathname, path);
81
82 /* Avoid UNC-path "//name" on Cygwin. */
83 if (len1 > 0 && pathname[len1 - 1] != '/')
84 strcat (pathname, "/");
85
86 strcat (pathname, dp->d_name);
87
88 struct stat st;
89 if (stat (pathname, &st))
90 return false;
91 return S_ISDIR (st.st_mode);
92 }
93
94 static bool _IsFile(const char *path, dirent *dp)
95 {
96 return !_IsDir(path, dp);
97 }
98 #endif // DT_DIR
99
100 #endif // !_WIN32
101
102 // returns list of *plain* file names in given directory,
103 // without paths, and without anything else
GetFileList(const char * path,StringList & files)104 bool GetFileList(const char *path, StringList& files)
105 {
106 #if !_WIN32
107 DIR * dirp;
108 struct dirent * dp;
109 dirp = opendir(path);
110 if(!dirp)
111 return false;
112
113 while((dp=readdir(dirp)) != NULL)
114 {
115 if (_IsFile(path, dp)) // only add if it is not a directory
116 {
117 std::string s(dp->d_name);
118 files.push_back(s);
119 }
120 }
121 closedir(dirp);
122 return true;
123
124 #else
125
126 WIN32_FIND_DATA fil;
127 std::string search(path);
128 MakeSlashTerminated(search);
129 search += "*";
130 HANDLE hFil = FindFirstFile(search.c_str(),&fil);
131 if(hFil == INVALID_HANDLE_VALUE)
132 return false;
133
134 do
135 {
136 if(!(fil.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
137 {
138 std::string s(fil.cFileName);
139 files.push_back(s);
140 }
141 }
142 while(FindNextFile(hFil, &fil));
143
144 FindClose(hFil);
145 return true;
146
147 #endif
148 }
149
150 // returns a list of directory names in the given directory, *without* the source dir.
151 // if getting the dir list recursively, all paths are added, except *again* the top source dir beeing queried.
GetDirList(const char * path,StringList & dirs,int depth)152 bool GetDirList(const char *path, StringList &dirs, int depth /* = 0 */)
153 {
154 #if !_WIN32
155 DIR * dirp;
156 struct dirent * dp;
157 dirp = opendir(path);
158 if(!dirp)
159 return false;
160
161 std::string pathstr(path);
162 MakeSlashTerminated(pathstr);
163 while((dp = readdir(dirp))) // assignment is intentional
164 {
165 if (_IsDir(path, dp)) // only add if it is a directory
166 {
167 if(strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0)
168 {
169 dirs.push_back(dp->d_name);
170 if (depth) // needing a better way to do that
171 {
172 std::string d = dp->d_name;
173 std::string subdir = pathstr + d;
174 MakeSlashTerminated(d);
175 StringList newdirs;
176 GetDirList(subdir.c_str(), newdirs, depth - 1);
177 for(std::deque<std::string>::iterator it = newdirs.begin(); it != newdirs.end(); ++it)
178 dirs.push_back(d + *it);
179 }
180 }
181 }
182 }
183 closedir(dirp);
184 return true;
185
186 #else
187
188 std::string pathstr(path);
189 MakeSlashTerminated(pathstr);
190 WIN32_FIND_DATA fil;
191 HANDLE hFil = FindFirstFile((pathstr + '*').c_str(),&fil);
192 if(hFil == INVALID_HANDLE_VALUE)
193 return false;
194
195 do
196 {
197 if( fil.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
198 {
199 if (!strcmp(fil.cFileName, ".") || !strcmp(fil.cFileName, ".."))
200 continue;
201
202 dirs.push_back(fil.cFileName);
203
204 if (depth) // need a better way to do that
205 {
206 std::string d = fil.cFileName;
207 std::string subdir = pathstr + d;
208 MakeSlashTerminated(d);
209 StringList newdirs;
210 GetDirList(subdir.c_str(), newdirs, depth - 1);
211 for(std::deque<std::string>::iterator it = newdirs.begin(); it != newdirs.end(); ++it)
212 dirs.push_back(d + *it);
213 }
214 }
215 }
216 while(FindNextFile(hFil, &fil));
217
218 FindClose(hFil);
219 return true;
220
221 #endif
222 }
223
FileExists(const char * fn)224 bool FileExists(const char *fn)
225 {
226 #if _WIN32
227 return _access(fn, 0) == 0;
228 #else
229 return access(fn, F_OK) == 0;
230 #endif
231 }
232
233 // must return true if creating the directory was successful, or already exists
CreateDir(const char * dir)234 bool CreateDir(const char *dir)
235 {
236 if(IsDirectory(dir)) // do not try to create if it already exists
237 return true;
238 bool result;
239 # if _WIN32
240 result = !!::CreateDirectory(dir, NULL);
241 # else
242 result = !mkdir(dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
243 #endif
244 return result;
245 }
246
CreateDirRec(const char * dir)247 bool CreateDirRec(const char *dir)
248 {
249 if(IsDirectory(dir))
250 return true;
251 bool result = true;
252 StringList li;
253 StrSplit(dir, "/\\", li, false);
254 std::string d;
255 d.reserve(strlen(dir) + 1);
256 if(*dir == '/')
257 d += '/';
258 bool last = false;
259 for(StringList::iterator it = li.begin(); it != li.end(); ++it)
260 {
261 d += *it;
262 last = CreateDir(d.c_str());
263 result = last && result;
264 d += '/';
265 }
266 return result || last;
267 }
268
GetFileSize(const char * fn,vfspos & size)269 bool GetFileSize(const char* fn, vfspos& size)
270 {
271 vfspos sz = 0;
272 #if defined(VFS_LARGEFILE_SUPPORT) && defined(_MSC_VER)
273 struct _stat64 st;
274 if(_stat64(fn, &st))
275 return false;
276 sz = st.st_size;
277 #else
278 struct stat st;
279 if(stat(fn, &st))
280 return false;
281 sz = st.st_size;
282 #endif
283 size = sz;
284 return true;
285 }
286
FixSlashes(std::string & s)287 void FixSlashes(std::string& s)
288 {
289 char last = 0, cur;
290 size_t wpos = 0;
291 for(size_t i = 0; i < s.length(); ++i)
292 {
293 cur = s[i];
294 if(cur == '\\')
295 cur = '/';
296 if(last == '/' && cur == '/')
297 continue;
298 s[wpos++] = cur;
299 last = cur;
300 }
301 s.resize(wpos);
302 }
303
FixPath(std::string & s)304 void FixPath(std::string& s)
305 {
306 if(s.empty())
307 return;
308 const char *p = s.c_str();
309 while(p[0] == '.' && (p[1] == '/' || p[1] == '\\'))
310 p += 2;
311 if(!*p)
312 {
313 s.clear();
314 return;
315 }
316 if(s.c_str() != p)
317 s = p;
318 size_t len = s.length();
319 while(len > 1) // remove all trailing slashes unless the first char is a slash -- leave it there for absolute unix paths
320 {
321 char end = s[len - 1];
322 if(end == '/' || end == '\\') // strip trailing '/'
323 --len;
324 else
325 break;
326 }
327 s.resize(len);
328 FixSlashes(s);
329 }
330
IsDirectory(const char * s)331 bool IsDirectory(const char *s)
332 {
333 #if _WIN32
334 DWORD dwFileAttr = GetFileAttributes(s);
335 if(dwFileAttr == INVALID_FILE_ATTRIBUTES)
336 return false;
337 return !!(dwFileAttr & FILE_ATTRIBUTE_DIRECTORY);
338 #else
339 if ( access( s, 0 ) == 0 )
340 {
341 struct stat status;
342 stat( s, &status );
343 return status.st_mode & S_IFDIR; // FIXME: what about symlinks here?
344 }
345 return false;
346 #endif
347 }
348
MakeSlashTerminated(std::string & s)349 void MakeSlashTerminated(std::string& s)
350 {
351 if(s.length() && s[s.length() - 1] != '/')
352 s += '/';
353 }
354
355 // extracts the file name (part after the last /) from a given path
356 // returns the string "/" as-is.
GetBaseNameFromPath(const char * str)357 const char *GetBaseNameFromPath(const char *str)
358 {
359 if(str[0] == '/' && !str[1])
360 return str;
361 const char *p = strrchr(str, '/');
362 return p ? p+1 : str;
363 }
364
StripFileExtension(std::string & s)365 void StripFileExtension(std::string& s)
366 {
367 size_t pos = s.find_last_of('.');
368 size_t pos2 = s.find_last_of('/');
369 if(pos != std::string::npos && (pos2 == std::string::npos || pos2 < pos))
370 s.resize(pos+1);
371 }
372
StripLastPath(std::string & s)373 void StripLastPath(std::string& s)
374 {
375 size_t len = s.length();
376 while(len)
377 {
378 char end = s[len - 1];
379 if(end == '/' || end == '\\') // strip trailing '/'
380 --len;
381 else
382 break;
383 }
384 s.resize(len);
385
386 if(s.empty())
387 return;
388
389 size_t pos = s.find_last_of('/');
390 if(pos == std::string::npos)
391 {
392 s.clear();
393 return; // nothing remains
394 }
395
396 s.resize(pos+1);
397 }
398
399
400 // from http://board.byuu.org/viewtopic.php?f=10&t=1089&start=15
WildcardMatch(const char * str,const char * pattern)401 bool WildcardMatch(const char *str, const char *pattern)
402 {
403 const char *cp = 0, *mp = 0;
404 while(*str && *pattern != '*')
405 {
406 if(*pattern != *str && *pattern != '?')
407 return false;
408 ++pattern;
409 ++str;
410 }
411
412 while(*str)
413 {
414 if(*pattern == '*')
415 {
416 if(!*++pattern)
417 return true;
418 mp = pattern;
419 cp = str + 1;
420 }
421 else if(*pattern == *str || *pattern == '?')
422 {
423 ++pattern;
424 ++str;
425 }
426 else
427 {
428 pattern = mp;
429 str = cp++;
430 }
431 }
432
433 while(*pattern == '*')
434 ++pattern;
435
436 return !*pattern;
437 }
438
439 // copy strings, mangling newlines to system standard
440 // windows has 13+10
441 // *nix has 10
442 // exotic systems may have 10+13
strnNLcpy(char * dst,const char * src,unsigned int n)443 size_t strnNLcpy(char *dst, const char *src, unsigned int n /* = -1 */)
444 {
445 char *olddst = dst;
446 bool had10 = false, had13 = false;
447
448 --n; // reserve 1 for \0 at end
449
450 while(*src && n)
451 {
452 if((had13 && *src == 10) || (had10 && *src == 13))
453 {
454 ++src; // last was already mangled
455 had13 = had10 = false; // processed one CRLF pair
456 continue;
457 }
458 had10 = *src == 10;
459 had13 = *src == 13;
460
461 if(had10 || had13)
462 {
463 *dst++ = '\n';
464 ++src;
465 }
466 else
467 *dst++ = *src++;
468
469 --n;
470 }
471
472 *dst++ = 0;
473
474 return dst - olddst;
475 }
476
477
478
479 VFS_NAMESPACE_END
480