3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
31 #ifdef _WIN32 // WINDOWS
33 #define _WIN32_WINNT 0x0501
39 #define BUFSIZE MAX_PATH
41 std::vector<DirListNode> GetDirListing(std::string pathstring)
43 std::vector<DirListNode> listing;
45 WIN32_FIND_DATA FindFileData;
46 HANDLE hFind = INVALID_HANDLE_VALUE;
51 DirSpec = (LPTSTR) malloc (BUFSIZE);
55 errorstream<<"GetDirListing: Insufficient memory available"<<std::endl;
60 // Check that the input is not larger than allowed.
61 if (pathstring.size() > (BUFSIZE - 2))
63 errorstream<<"GetDirListing: Input directory is too large."<<std::endl;
68 //_tprintf (TEXT("Target directory is %s.\n"), pathstring.c_str());
70 sprintf(DirSpec, "%s", (pathstring + "\\*").c_str());
72 // Find the first file in the directory.
73 hFind = FindFirstFile(DirSpec, &FindFileData);
75 if (hFind == INVALID_HANDLE_VALUE)
83 // Be very sure to not include '..' in the results, it will
84 // result in an epic failure when deleting stuff.
87 node.name = FindFileData.cFileName;
88 node.dir = FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
89 if(node.name != "." && node.name != "..")
90 listing.push_back(node);
92 // List all the other files in the directory.
93 while (FindNextFile(hFind, &FindFileData) != 0)
96 node.name = FindFileData.cFileName;
97 node.dir = FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
98 if(node.name != "." && node.name != "..")
99 listing.push_back(node);
102 dwError = GetLastError();
104 if (dwError != ERROR_NO_MORE_FILES)
106 errorstream<<"GetDirListing: FindNextFile error. Error is "
107 <<dwError<<std::endl;
117 if(retval != 0) listing.clear();
119 //for(unsigned int i=0; i<listing.size(); i++){
120 // infostream<<listing[i].name<<(listing[i].dir?" (dir)":" (file)")<<std::endl;
126 bool CreateDir(std::string path)
128 bool r = CreateDirectory(path.c_str(), NULL);
131 if(GetLastError() == ERROR_ALREADY_EXISTS)
136 bool PathExists(std::string path)
138 return (GetFileAttributes(path.c_str()) != INVALID_FILE_ATTRIBUTES);
141 bool IsDir(std::string path)
143 DWORD attr = GetFileAttributes(path.c_str());
144 return (attr != INVALID_FILE_ATTRIBUTES &&
145 (attr & FILE_ATTRIBUTE_DIRECTORY));
148 bool IsDirDelimiter(char c)
150 return c == '/' || c == '\\';
153 bool RecursiveDelete(std::string path)
155 infostream<<"Recursively deleting \""<<path<<"\""<<std::endl;
157 DWORD attr = GetFileAttributes(path.c_str());
158 bool is_directory = (attr != INVALID_FILE_ATTRIBUTES &&
159 (attr & FILE_ATTRIBUTE_DIRECTORY));
162 infostream<<"RecursiveDelete: Deleting file "<<path<<std::endl;
163 //bool did = DeleteFile(path.c_str());
166 errorstream<<"RecursiveDelete: Failed to delete file "
173 infostream<<"RecursiveDelete: Deleting content of directory "
175 std::vector<DirListNode> content = GetDirListing(path);
176 for(int i=0; i<content.size(); i++){
177 const DirListNode &n = content[i];
178 std::string fullpath = path + DIR_DELIM + n.name;
179 bool did = RecursiveDelete(fullpath);
181 errorstream<<"RecursiveDelete: Failed to recurse to "
182 <<fullpath<<std::endl;
186 infostream<<"RecursiveDelete: Deleting directory "<<path<<std::endl;
187 //bool did = RemoveDirectory(path.c_str();
190 errorstream<<"Failed to recursively delete directory "
198 bool DeleteSingleFileOrEmptyDirectory(std::string path)
200 DWORD attr = GetFileAttributes(path.c_str());
201 bool is_directory = (attr != INVALID_FILE_ATTRIBUTES &&
202 (attr & FILE_ATTRIBUTE_DIRECTORY));
205 bool did = DeleteFile(path.c_str());
210 bool did = RemoveDirectory(path.c_str());
215 std::string TempPath()
217 DWORD bufsize = GetTempPath(0, "");
219 errorstream<<"GetTempPath failed, error = "<<GetLastError()<<std::endl;
222 std::vector<char> buf(bufsize);
223 DWORD len = GetTempPath(bufsize, &buf[0]);
224 if(len == 0 || len > bufsize){
225 errorstream<<"GetTempPath failed, error = "<<GetLastError()<<std::endl;
228 return std::string(buf.begin(), buf.begin() + len);
233 #include <sys/types.h>
235 #include <sys/stat.h>
236 #include <sys/wait.h>
239 std::vector<DirListNode> GetDirListing(std::string pathstring)
241 std::vector<DirListNode> listing;
245 if((dp = opendir(pathstring.c_str())) == NULL) {
246 //infostream<<"Error("<<errno<<") opening "<<pathstring<<std::endl;
250 while ((dirp = readdir(dp)) != NULL) {
252 // Be very sure to not include '..' in the results, it will
253 // result in an epic failure when deleting stuff.
254 if(dirp->d_name[0]!='.'){
256 node.name = dirp->d_name;
257 if(node.name == "." || node.name == "..")
260 int isdir = -1; // -1 means unknown
263 POSIX doesn't define d_type member of struct dirent and
264 certain filesystems on glibc/Linux will only return
265 DT_UNKNOWN for the d_type member.
267 Also we don't know whether symlinks are directories or not.
269 #ifdef _DIRENT_HAVE_D_TYPE
270 if(dirp->d_type != DT_UNKNOWN && dirp->d_type != DT_LNK)
271 isdir = (dirp->d_type == DT_DIR);
272 #endif /* _DIRENT_HAVE_D_TYPE */
275 Was d_type DT_UNKNOWN, DT_LNK or nonexistent?
281 if (stat((pathstring + "/" + node.name).c_str(), &statbuf))
283 isdir = ((statbuf.st_mode & S_IFDIR) == S_IFDIR);
286 listing.push_back(node);
294 bool CreateDir(std::string path)
296 int r = mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
303 // If already exists, return true
310 bool PathExists(std::string path)
313 return (stat(path.c_str(),&st) == 0);
316 bool IsDir(std::string path)
319 if(stat(path.c_str(), &statbuf))
320 return false; // Actually error; but certainly not a directory
321 return ((statbuf.st_mode & S_IFDIR) == S_IFDIR);
324 bool IsDirDelimiter(char c)
329 bool RecursiveDelete(std::string path)
332 Execute the 'rm' command directly, by fork() and execve()
335 infostream<<"Removing \""<<path<<"\""<<std::endl;
339 pid_t child_pid = fork();
344 char argv_data[3][10000];
345 strcpy(argv_data[0], "/bin/rm");
346 strcpy(argv_data[1], "-rf");
347 strncpy(argv_data[2], path.c_str(), 10000);
349 argv[0] = argv_data[0];
350 argv[1] = argv_data[1];
351 argv[2] = argv_data[2];
354 verbosestream<<"Executing '"<<argv[0]<<"' '"<<argv[1]<<"' '"
355 <<argv[2]<<"'"<<std::endl;
357 execv(argv[0], argv);
359 // Execv shouldn't return. Failed.
368 tpid = wait(&child_status);
369 //if(tpid != child_pid) process_terminated(tpid);
370 }while(tpid != child_pid);
371 return (child_status == 0);
375 bool DeleteSingleFileOrEmptyDirectory(std::string path)
378 bool did = (rmdir(path.c_str()) == 0);
380 errorstream<<"rmdir errno: "<<errno<<": "<<strerror(errno)
384 bool did = (unlink(path.c_str()) == 0);
386 errorstream<<"unlink errno: "<<errno<<": "<<strerror(errno)
392 std::string TempPath()
395 Should the environment variables TMPDIR, TMP and TEMP
396 and the macro P_tmpdir (if defined by stdio.h) be checked
397 before falling back on /tmp?
399 Probably not, because this function is intended to be
400 compatible with lua's os.tmpname which under the default
401 configuration hardcodes mkstemp("/tmp/lua_XXXXXX").
403 return std::string(DIR_DELIM) + "tmp";
408 void GetRecursiveSubPaths(std::string path, std::vector<std::string> &dst)
410 std::vector<DirListNode> content = GetDirListing(path);
411 for(unsigned int i=0; i<content.size(); i++){
412 const DirListNode &n = content[i];
413 std::string fullpath = path + DIR_DELIM + n.name;
414 dst.push_back(fullpath);
415 GetRecursiveSubPaths(fullpath, dst);
419 bool DeletePaths(const std::vector<std::string> &paths)
422 // Go backwards to succesfully delete the output of GetRecursiveSubPaths
423 for(int i=paths.size()-1; i>=0; i--){
424 const std::string &path = paths[i];
425 bool did = DeleteSingleFileOrEmptyDirectory(path);
427 errorstream<<"Failed to delete "<<path<<std::endl;
434 bool RecursiveDeleteContent(std::string path)
436 infostream<<"Removing content of \""<<path<<"\""<<std::endl;
437 std::vector<DirListNode> list = GetDirListing(path);
438 for(unsigned int i=0; i<list.size(); i++)
440 if(trim(list[i].name) == "." || trim(list[i].name) == "..")
442 std::string childpath = path + DIR_DELIM + list[i].name;
443 bool r = RecursiveDelete(childpath);
446 errorstream<<"Removing \""<<childpath<<"\" failed"<<std::endl;
453 bool CreateAllDirs(std::string path)
456 std::vector<std::string> tocreate;
457 std::string basepath = path;
458 while(!PathExists(basepath))
460 tocreate.push_back(basepath);
461 basepath = RemoveLastPathComponent(basepath);
465 for(int i=tocreate.size()-1;i>=0;i--)
466 if(!CreateDir(tocreate[i]))
471 bool CopyFileContents(std::string source, std::string target)
473 FILE *sourcefile = fopen(source.c_str(), "rb");
474 if(sourcefile == NULL){
475 errorstream<<source<<": can't open for reading: "
476 <<strerror(errno)<<std::endl;
480 FILE *targetfile = fopen(target.c_str(), "wb");
481 if(targetfile == NULL){
482 errorstream<<target<<": can't open for writing: "
483 <<strerror(errno)<<std::endl;
491 char readbuffer[BUFSIZ];
493 size_t readbytes = fread(readbuffer, 1,
494 sizeof(readbuffer), sourcefile);
496 if(ferror(sourcefile)){
497 errorstream<<source<<": IO error: "
498 <<strerror(errno)<<std::endl;
503 fwrite(readbuffer, 1, readbytes, targetfile);
505 if(feof(sourcefile) || ferror(sourcefile)){
506 // flush destination file to catch write errors
511 if(ferror(targetfile)){
512 errorstream<<target<<": IO error: "
513 <<strerror(errno)<<std::endl;
518 infostream<<"copied "<<total<<" bytes from "
519 <<source<<" to "<<target<<std::endl;
525 bool CopyDir(std::string source, std::string target)
527 if(PathExists(source)){
528 if(!PathExists(target)){
529 fs::CreateAllDirs(target);
532 std::vector<DirListNode> content = fs::GetDirListing(source);
534 for(unsigned int i=0; i < content.size(); i++){
535 std::string sourcechild = source + DIR_DELIM + content[i].name;
536 std::string targetchild = target + DIR_DELIM + content[i].name;
538 if(!fs::CopyDir(sourcechild, targetchild)){
543 if(!fs::CopyFileContents(sourcechild, targetchild)){
555 bool PathStartsWith(std::string path, std::string prefix)
557 size_t pathsize = path.size();
559 size_t prefixsize = prefix.size();
560 size_t prefixpos = 0;
562 bool delim1 = pathpos == pathsize
563 || IsDirDelimiter(path[pathpos]);
564 bool delim2 = prefixpos == prefixsize
565 || IsDirDelimiter(prefix[prefixpos]);
571 while(pathpos < pathsize &&
572 IsDirDelimiter(path[pathpos]))
574 while(prefixpos < prefixsize &&
575 IsDirDelimiter(prefix[prefixpos]))
577 if(prefixpos == prefixsize)
579 if(pathpos == pathsize)
585 char pathchar = path[pathpos+len];
586 char prefixchar = prefix[prefixpos+len];
587 if(FILESYS_CASE_INSENSITIVE){
588 pathchar = tolower(pathchar);
589 prefixchar = tolower(prefixchar);
591 if(pathchar != prefixchar)
594 } while(pathpos+len < pathsize
595 && !IsDirDelimiter(path[pathpos+len])
596 && prefixpos+len < prefixsize
598 prefix[prefixsize+len]));
605 std::string RemoveLastPathComponent(std::string path,
606 std::string *removed, int count)
611 size_t remaining = path.size();
613 for(int i = 0; i < count; ++i){
614 // strip a dir delimiter
615 while(remaining != 0 && IsDirDelimiter(path[remaining-1]))
617 // strip a path component
618 size_t component_end = remaining;
619 while(remaining != 0 && !IsDirDelimiter(path[remaining-1]))
621 size_t component_start = remaining;
622 // strip a dir delimiter
623 while(remaining != 0 && IsDirDelimiter(path[remaining-1]))
626 std::string component = path.substr(component_start,
627 component_end - component_start);
629 *removed = component + DIR_DELIM + *removed;
631 *removed = component;
634 return path.substr(0, remaining);
637 std::string RemoveRelativePathComponents(std::string path)
639 size_t pos = path.size();
640 size_t dotdot_count = 0;
642 size_t component_with_delim_end = pos;
643 // skip a dir delimiter
644 while(pos != 0 && IsDirDelimiter(path[pos-1]))
646 // strip a path component
647 size_t component_end = pos;
648 while(pos != 0 && !IsDirDelimiter(path[pos-1]))
650 size_t component_start = pos;
652 std::string component = path.substr(component_start,
653 component_end - component_start);
654 bool remove_this_component = false;
655 if(component == "."){
656 remove_this_component = true;
658 else if(component == ".."){
659 remove_this_component = true;
662 else if(dotdot_count != 0){
663 remove_this_component = true;
667 if(remove_this_component){
668 while(pos != 0 && IsDirDelimiter(path[pos-1]))
670 path = path.substr(0, pos) + DIR_DELIM +
671 path.substr(component_with_delim_end,
680 // remove trailing dir delimiters
682 while(pos != 0 && IsDirDelimiter(path[pos-1]))
684 return path.substr(0, pos);