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.
21 #include "util/string.h"
33 #ifdef _WIN32 // WINDOWS
35 #define _WIN32_WINNT 0x0501
41 #define BUFSIZE MAX_PATH
43 std::vector<DirListNode> GetDirListing(std::string pathstring)
45 std::vector<DirListNode> listing;
47 WIN32_FIND_DATA FindFileData;
48 HANDLE hFind = INVALID_HANDLE_VALUE;
53 DirSpec = (LPTSTR) malloc (BUFSIZE);
56 errorstream<<"GetDirListing: Insufficient memory available"<<std::endl;
61 // Check that the input is not larger than allowed.
62 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) {
80 // Be very sure to not include '..' in the results, it will
81 // result in an epic failure when deleting stuff.
84 node.name = FindFileData.cFileName;
85 node.dir = FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
86 if(node.name != "." && node.name != "..")
87 listing.push_back(node);
89 // List all the other files in the directory.
90 while (FindNextFile(hFind, &FindFileData) != 0) {
92 node.name = FindFileData.cFileName;
93 node.dir = FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
94 if(node.name != "." && node.name != "..")
95 listing.push_back(node);
98 dwError = GetLastError();
100 if (dwError != ERROR_NO_MORE_FILES) {
101 errorstream<<"GetDirListing: FindNextFile error. Error is "
102 <<dwError<<std::endl;
112 if(retval != 0) listing.clear();
114 //for(unsigned int i=0; i<listing.size(); i++){
115 // infostream<<listing[i].name<<(listing[i].dir?" (dir)":" (file)")<<std::endl;
121 bool CreateDir(std::string path)
123 bool r = CreateDirectory(path.c_str(), NULL);
126 if(GetLastError() == ERROR_ALREADY_EXISTS)
131 bool PathExists(std::string path)
133 return (GetFileAttributes(path.c_str()) != INVALID_FILE_ATTRIBUTES);
136 bool IsDir(std::string path)
138 DWORD attr = GetFileAttributes(path.c_str());
139 return (attr != INVALID_FILE_ATTRIBUTES &&
140 (attr & FILE_ATTRIBUTE_DIRECTORY));
143 bool IsDirDelimiter(char c)
145 return c == '/' || c == '\\';
148 bool RecursiveDelete(std::string path)
150 infostream<<"Recursively deleting \""<<path<<"\""<<std::endl;
152 DWORD attr = GetFileAttributes(path.c_str());
153 bool is_directory = (attr != INVALID_FILE_ATTRIBUTES &&
154 (attr & FILE_ATTRIBUTE_DIRECTORY));
157 infostream<<"RecursiveDelete: Deleting file "<<path<<std::endl;
158 //bool did = DeleteFile(path.c_str());
161 errorstream<<"RecursiveDelete: Failed to delete file "
168 infostream<<"RecursiveDelete: Deleting content of directory "
170 std::vector<DirListNode> content = GetDirListing(path);
171 for(int i=0; i<content.size(); i++){
172 const DirListNode &n = content[i];
173 std::string fullpath = path + DIR_DELIM + n.name;
174 bool did = RecursiveDelete(fullpath);
176 errorstream<<"RecursiveDelete: Failed to recurse to "
177 <<fullpath<<std::endl;
181 infostream<<"RecursiveDelete: Deleting directory "<<path<<std::endl;
182 //bool did = RemoveDirectory(path.c_str();
185 errorstream<<"Failed to recursively delete directory "
193 bool DeleteSingleFileOrEmptyDirectory(std::string path)
195 DWORD attr = GetFileAttributes(path.c_str());
196 bool is_directory = (attr != INVALID_FILE_ATTRIBUTES &&
197 (attr & FILE_ATTRIBUTE_DIRECTORY));
200 bool did = DeleteFile(path.c_str());
205 bool did = RemoveDirectory(path.c_str());
210 std::string TempPath()
212 DWORD bufsize = GetTempPath(0, "");
214 errorstream<<"GetTempPath failed, error = "<<GetLastError()<<std::endl;
217 std::vector<char> buf(bufsize);
218 DWORD len = GetTempPath(bufsize, &buf[0]);
219 if(len == 0 || len > bufsize){
220 errorstream<<"GetTempPath failed, error = "<<GetLastError()<<std::endl;
223 return std::string(buf.begin(), buf.begin() + len);
228 #include <sys/types.h>
230 #include <sys/stat.h>
231 #include <sys/wait.h>
234 std::vector<DirListNode> GetDirListing(std::string pathstring)
236 std::vector<DirListNode> listing;
240 if((dp = opendir(pathstring.c_str())) == NULL) {
241 //infostream<<"Error("<<errno<<") opening "<<pathstring<<std::endl;
245 while ((dirp = readdir(dp)) != NULL) {
247 // Be very sure to not include '..' in the results, it will
248 // result in an epic failure when deleting stuff.
249 if(dirp->d_name == "." || dirp->d_name == "..")
253 node.name = dirp->d_name;
255 int isdir = -1; // -1 means unknown
258 POSIX doesn't define d_type member of struct dirent and
259 certain filesystems on glibc/Linux will only return
260 DT_UNKNOWN for the d_type member.
262 Also we don't know whether symlinks are directories or not.
264 #ifdef _DIRENT_HAVE_D_TYPE
265 if(dirp->d_type != DT_UNKNOWN && dirp->d_type != DT_LNK)
266 isdir = (dirp->d_type == DT_DIR);
267 #endif /* _DIRENT_HAVE_D_TYPE */
270 Was d_type DT_UNKNOWN, DT_LNK or nonexistent?
275 if (stat((pathstring + "/" + node.name).c_str(), &statbuf))
277 isdir = ((statbuf.st_mode & S_IFDIR) == S_IFDIR);
280 listing.push_back(node);
287 bool CreateDir(std::string path)
289 int r = mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
296 // If already exists, return true
303 bool PathExists(std::string path)
306 return (stat(path.c_str(),&st) == 0);
309 bool IsDir(std::string path)
312 if(stat(path.c_str(), &statbuf))
313 return false; // Actually error; but certainly not a directory
314 return ((statbuf.st_mode & S_IFDIR) == S_IFDIR);
317 bool IsDirDelimiter(char c)
322 bool RecursiveDelete(std::string path)
325 Execute the 'rm' command directly, by fork() and execve()
328 infostream<<"Removing \""<<path<<"\""<<std::endl;
332 pid_t child_pid = fork();
337 char argv_data[3][10000];
338 strcpy(argv_data[0], "/bin/rm");
339 strcpy(argv_data[1], "-rf");
340 strncpy(argv_data[2], path.c_str(), 10000);
342 argv[0] = argv_data[0];
343 argv[1] = argv_data[1];
344 argv[2] = argv_data[2];
347 verbosestream<<"Executing '"<<argv[0]<<"' '"<<argv[1]<<"' '"
348 <<argv[2]<<"'"<<std::endl;
350 execv(argv[0], argv);
352 // Execv shouldn't return. Failed.
361 tpid = wait(&child_status);
362 //if(tpid != child_pid) process_terminated(tpid);
363 }while(tpid != child_pid);
364 return (child_status == 0);
368 bool DeleteSingleFileOrEmptyDirectory(std::string path)
371 bool did = (rmdir(path.c_str()) == 0);
373 errorstream<<"rmdir errno: "<<errno<<": "<<strerror(errno)
377 bool did = (unlink(path.c_str()) == 0);
379 errorstream<<"unlink errno: "<<errno<<": "<<strerror(errno)
385 std::string TempPath()
388 Should the environment variables TMPDIR, TMP and TEMP
389 and the macro P_tmpdir (if defined by stdio.h) be checked
390 before falling back on /tmp?
392 Probably not, because this function is intended to be
393 compatible with lua's os.tmpname which under the default
394 configuration hardcodes mkstemp("/tmp/lua_XXXXXX").
397 return DIR_DELIM "sdcard" DIR_DELIM PROJECT_NAME DIR_DELIM "tmp";
399 return DIR_DELIM "tmp";
405 void GetRecursiveSubPaths(std::string path, std::vector<std::string> &dst)
407 std::vector<DirListNode> content = GetDirListing(path);
408 for(unsigned int i=0; i<content.size(); i++){
409 const DirListNode &n = content[i];
410 std::string fullpath = path + DIR_DELIM + n.name;
411 dst.push_back(fullpath);
412 GetRecursiveSubPaths(fullpath, dst);
416 bool DeletePaths(const std::vector<std::string> &paths)
419 // Go backwards to succesfully delete the output of GetRecursiveSubPaths
420 for(int i=paths.size()-1; i>=0; i--){
421 const std::string &path = paths[i];
422 bool did = DeleteSingleFileOrEmptyDirectory(path);
424 errorstream<<"Failed to delete "<<path<<std::endl;
431 bool RecursiveDeleteContent(std::string path)
433 infostream<<"Removing content of \""<<path<<"\""<<std::endl;
434 std::vector<DirListNode> list = GetDirListing(path);
435 for(unsigned int i=0; i<list.size(); i++)
437 if(trim(list[i].name) == "." || trim(list[i].name) == "..")
439 std::string childpath = path + DIR_DELIM + list[i].name;
440 bool r = RecursiveDelete(childpath);
443 errorstream<<"Removing \""<<childpath<<"\" failed"<<std::endl;
450 bool CreateAllDirs(std::string path)
453 std::vector<std::string> tocreate;
454 std::string basepath = path;
455 while(!PathExists(basepath))
457 tocreate.push_back(basepath);
458 basepath = RemoveLastPathComponent(basepath);
462 for(int i=tocreate.size()-1;i>=0;i--)
463 if(!CreateDir(tocreate[i]))
468 bool CopyFileContents(std::string source, std::string target)
470 FILE *sourcefile = fopen(source.c_str(), "rb");
471 if(sourcefile == NULL){
472 errorstream<<source<<": can't open for reading: "
473 <<strerror(errno)<<std::endl;
477 FILE *targetfile = fopen(target.c_str(), "wb");
478 if(targetfile == NULL){
479 errorstream<<target<<": can't open for writing: "
480 <<strerror(errno)<<std::endl;
488 char readbuffer[BUFSIZ];
490 size_t readbytes = fread(readbuffer, 1,
491 sizeof(readbuffer), sourcefile);
493 if(ferror(sourcefile)){
494 errorstream<<source<<": IO error: "
495 <<strerror(errno)<<std::endl;
500 fwrite(readbuffer, 1, readbytes, targetfile);
502 if(feof(sourcefile) || ferror(sourcefile)){
503 // flush destination file to catch write errors
508 if(ferror(targetfile)){
509 errorstream<<target<<": IO error: "
510 <<strerror(errno)<<std::endl;
515 infostream<<"copied "<<total<<" bytes from "
516 <<source<<" to "<<target<<std::endl;
522 bool CopyDir(std::string source, std::string target)
524 if(PathExists(source)){
525 if(!PathExists(target)){
526 fs::CreateAllDirs(target);
529 std::vector<DirListNode> content = fs::GetDirListing(source);
531 for(unsigned int i=0; i < content.size(); i++){
532 std::string sourcechild = source + DIR_DELIM + content[i].name;
533 std::string targetchild = target + DIR_DELIM + content[i].name;
535 if(!fs::CopyDir(sourcechild, targetchild)){
540 if(!fs::CopyFileContents(sourcechild, targetchild)){
552 bool PathStartsWith(std::string path, std::string prefix)
554 size_t pathsize = path.size();
556 size_t prefixsize = prefix.size();
557 size_t prefixpos = 0;
559 bool delim1 = pathpos == pathsize
560 || IsDirDelimiter(path[pathpos]);
561 bool delim2 = prefixpos == prefixsize
562 || IsDirDelimiter(prefix[prefixpos]);
568 while(pathpos < pathsize &&
569 IsDirDelimiter(path[pathpos]))
571 while(prefixpos < prefixsize &&
572 IsDirDelimiter(prefix[prefixpos]))
574 if(prefixpos == prefixsize)
576 if(pathpos == pathsize)
582 char pathchar = path[pathpos+len];
583 char prefixchar = prefix[prefixpos+len];
584 if(FILESYS_CASE_INSENSITIVE){
585 pathchar = tolower(pathchar);
586 prefixchar = tolower(prefixchar);
588 if(pathchar != prefixchar)
591 } while(pathpos+len < pathsize
592 && !IsDirDelimiter(path[pathpos+len])
593 && prefixpos+len < prefixsize
595 prefix[prefixpos+len]));
602 std::string RemoveLastPathComponent(std::string path,
603 std::string *removed, int count)
608 size_t remaining = path.size();
610 for(int i = 0; i < count; ++i){
611 // strip a dir delimiter
612 while(remaining != 0 && IsDirDelimiter(path[remaining-1]))
614 // strip a path component
615 size_t component_end = remaining;
616 while(remaining != 0 && !IsDirDelimiter(path[remaining-1]))
618 size_t component_start = remaining;
619 // strip a dir delimiter
620 while(remaining != 0 && IsDirDelimiter(path[remaining-1]))
623 std::string component = path.substr(component_start,
624 component_end - component_start);
626 *removed = component + DIR_DELIM + *removed;
628 *removed = component;
631 return path.substr(0, remaining);
634 std::string RemoveRelativePathComponents(std::string path)
636 size_t pos = path.size();
637 size_t dotdot_count = 0;
639 size_t component_with_delim_end = pos;
640 // skip a dir delimiter
641 while(pos != 0 && IsDirDelimiter(path[pos-1]))
643 // strip a path component
644 size_t component_end = pos;
645 while(pos != 0 && !IsDirDelimiter(path[pos-1]))
647 size_t component_start = pos;
649 std::string component = path.substr(component_start,
650 component_end - component_start);
651 bool remove_this_component = false;
652 if(component == "."){
653 remove_this_component = true;
655 else if(component == ".."){
656 remove_this_component = true;
659 else if(dotdot_count != 0){
660 remove_this_component = true;
664 if(remove_this_component){
665 while(pos != 0 && IsDirDelimiter(path[pos-1]))
667 path = path.substr(0, pos) + DIR_DELIM +
668 path.substr(component_with_delim_end,
677 // remove trailing dir delimiters
679 while(pos != 0 && IsDirDelimiter(path[pos-1]))
681 return path.substr(0, pos);
684 bool safeWriteToFile(const std::string &path, const std::string &content)
686 std::string tmp_file = path + ".~mt";
688 // Write to a tmp file
689 std::ofstream os(tmp_file.c_str(), std::ios::binary);
696 remove(tmp_file.c_str());
701 remove(path.c_str());
702 if(rename(tmp_file.c_str(), path.c_str())) {
703 remove(tmp_file.c_str());