/* PIP - Platform Independent Primitives Directory Ivan Pelipenko peri4ko@yandex.ru, Andrey Bychkov work.a.b@yandex.ru This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #include "piincludes_p.h" #include "pidir.h" const PIChar PIDir::separator = '/'; #ifdef QNX # define _stat_struct_ struct stat # define _stat_call_ stat # define _stat_link_ lstat #else # define _stat_struct_ struct stat64 # define _stat_call_ stat64 # define _stat_link_ lstat64 #endif #ifndef WINDOWS # ifdef ANDROID # include # else # ifdef FREERTOS extern "C" { # include } # else # include # endif # endif # include #endif /*! \class PIDir * \brief Local directory * * \section PIDir_sec0 Synopsis * This class provide access to local file. You can manipulate * binary content or use this class as text stream. To binary * access there are function \a read(), \a write(), and many * \a writeBinary() functions. For write variables to file in * their text representation threr are many "<<" operators. * */ PIDir::PIDir(const PIString & dir) { setDir(dir); } PIDir::PIDir(const PIFile & file) { setDir(file.path()); if (isExists()) return; int pos = path_.findLast(separator); path_.cutRight(path_.size_s() - pos); } bool PIDir::operator ==(const PIDir & d) const { return d.absolutePath() == absolutePath(); } bool PIDir::isAbsolute() const { if (path_.isEmpty()) return false; if (path_[0] == separator) return true; #ifdef WINDOWS return (path_.mid(1, 1) == ":"); #endif return false; } PIString PIDir::path() const { #ifdef WINDOWS if (path_.startsWith(separator)) { if (path_.length() == 1) return separator; else return path_.mid(1); } else #endif return path_; } PIString PIDir::absolutePath() const { if (isAbsolute()) { #ifdef WINDOWS if (path_.startsWith(separator)) { if (path_.length() == 1) return separator; else return path_.mid(1); } else #endif return path_; } return PIDir(PIDir::current().path() + separator + path_).path(); } PIDir & PIDir::cleanPath() { PIString p(path_); if (p.isEmpty()) { path_ = "."; return *this; } PIString sep = PIString(separator); path_.replaceAll(sep + sep, sep); bool is_abs = isAbsolute(); PIStringList l = PIString(p).split(separator); l.removeAll("."); l.removeAll(""); for (int i = 0; i < l.size_s() - 1; ++i) { if (l[i] != ".." && l[i + 1] == "..") { l.remove(i, 2); i -= 2; if (i < -1) i = -1; } } if (is_abs) while (!l.isEmpty()) { if (l.front() == "..") l.pop_front(); else break; } path_ = l.join(separator); if (is_abs) path_.prepend(separator); if (path_.isEmpty()) path_ = "."; return *this; } PIString PIDir::relative(const PIString & path) const { PIDir td(path); PIStringList dl(absolutePath().split(separator)), pl(td.absolutePath().split(separator)), rl; //piCout << pl << "rel to" << dl; while (!dl.isEmpty() && !pl.isEmpty()) { if (dl.front() != pl.front()) break; dl.pop_front(); pl.pop_front(); } for (int i = 0; i < dl.size_s(); ++i) rl << ".."; rl << pl; if (rl.isEmpty()) return "."; return rl.join(separator); } PIDir & PIDir::setDir(const PIString & path) { path_ = path; #ifdef WINDOWS path_.replaceAll("\\", separator); if (path_.length() > 2) if (path_.mid(1, 2).contains(":")) path_.prepend(separator); #endif cleanPath(); return *this; } PIDir & PIDir::cd(const PIString & path) { if (path_.isEmpty()) return *this; if (path_.back() != separator) path_ += separator; path_ += path; return cleanPath(); } bool PIDir::make(bool withParents) { PIDir d = cleanedPath(); //PIString tp; #ifndef WINDOWS bool is_abs = isAbsolute(); #endif if (withParents) { PIStringList l = d.path().split(separator); //piCout << l; l.removeAll(""); //piCout << l; PIString cdp; piForeachC (PIString & i, l) { if (!cdp.isEmpty() #ifndef WINDOWS || is_abs #endif ) cdp += separator; cdp += i; //piCout << "dir" << cdp; if (!isExists(cdp)) if (!makeDir(cdp)) return false; } /*for (int i = l.size_s() - 1; i >= 0; --i) { if (i > 1) tp = PIStringList(l).remove(i, l.size_s() - i).join(separator); else { tp = separator; if (!is_abs) tp.push_front('.'); } piCout << "check" << tp; if (isExists(tp)) { for (int j = i + 1; j <= l.size_s(); ++j) { tp = PIStringList(l).remove(j, l.size_s() - j).join(separator); piCout << "make" << tp; if (makeDir(tp)) continue; else return false; } break; }; }*/ return true; } else if (makeDir(d.path())) return true; return false; } #ifdef WINDOWS int sort_compare(const PIFile::FileInfo * v0, const PIFile::FileInfo * v1) { return strcoll(v0->path.data(), v1->path.data()); } #endif PIVector PIDir::entries() { PIVector l; if (!isExists()) return l; PIString dp = absolutePath(); PIString p(dp); if (dp == ".") dp.clear(); else if (!dp.endsWith(separator)) dp += separator; // piCout << "start entries from" << p; #ifdef WINDOWS if (dp == separator) { char letters[1024]; PIFile::FileInfo fi; DWORD ll = GetLogicalDriveStrings(1023, letters); PIString clet; for (DWORD i = 0; i < ll; ++i) { if (letters[i] == '\0') { clet.resize(2); fi.path = clet; fi.flags = PIFile::FileInfo::Dir; l << fi; clet.clear(); } else clet += PIChar(letters[i]); } } else { WIN32_FIND_DATA fd; memset(&fd, 0, sizeof(fd)); p += "\\*"; void * hf = FindFirstFile((LPCTSTR)(p.data()), &fd); if (!hf) return l; bool hdd = false; do { PIString fn(fd.cFileName); if (fn == "..") hdd = true; l << PIFile::fileInfo(dp + fn); memset(&fd, 0, sizeof(fd)); } while (FindNextFile(hf, &fd) != 0); FindClose(hf); l.sort(sort_compare); if (!hdd) { PIFile::FileInfo fi; fi.path = ".."; fi.flags = PIFile::FileInfo::Dir | PIFile::FileInfo::DotDot; l.push_front(fi); } } #else # if defined(QNX) || defined(FREERTOS) struct dirent * de = 0; DIR * dir = 0; dir = opendir(p.data()); if (dir) { for (;;) { de = readdir(dir); if (!de) break; l << PIFile::fileInfo(dp + PIString(de->d_name)); } closedir(dir); } # else dirent ** list; int cnt = scandir(p.data(), &list, 0, # if defined(MAC_OS) || defined(ANDROID) || defined(BLACKBERRY) alphasort); # else versionsort); # endif for (int i = 0; i < cnt; ++i) { l << PIFile::fileInfo(dp + PIString(list[i]->d_name)); free(list[i]); } free(list); # endif #endif // piCout << "end entries from" << p; return l; } PIVector PIDir::allEntries() { PIVector ret; PIVector dirs; PIStringList cdirs, ndirs; cdirs << path(); while (!cdirs.isEmpty()) { piForeachC (PIString & d, cdirs) { scan_ = d; PIVector el = PIDir(d).entries(); piForeachC (PIFile::FileInfo & de, el) { if (de.name() == "." || de.name() == "..") continue; if (de.isSymbolicLink()) continue; /// TODO: resolve symlinks if (de.isDir()) { dirs << de; ndirs << de.path; } else ret << de; } } cdirs = ndirs; ndirs.clear(); } ret.insert(0, dirs); scan_.clear(); return ret; } bool PIDir::isExists(const PIString & path) { #ifdef WINDOWS DWORD ret = GetFileAttributes((LPCTSTR)(path.data())); return (ret != 0xFFFFFFFF) && (ret & FILE_ATTRIBUTE_DIRECTORY); #else DIR * dir_ = opendir(path.data()); if (dir_ == 0) return false; closedir(dir_); #endif return true; } PIDir PIDir::current() { #ifndef ESP_PLATFORM char rc[1024]; #endif #ifdef WINDOWS memset(rc, 0, 1024); if (GetCurrentDirectory(1024, (LPTSTR)rc) == 0) return PIString(); PIString ret(rc); ret.replaceAll("\\", PIDir::separator); ret.prepend(separator); return PIDir(ret); #else # ifndef ESP_PLATFORM if (getcwd(rc, 1024) == 0) return PIString(); return PIDir(rc); # else return PIDir("/spiffs"); # endif #endif } PIDir PIDir::home() { #ifndef ESP_PLATFORM char * rc = 0; #endif #ifdef WINDOWS rc = new char[1024]; memset(rc, 0, 1024); if (ExpandEnvironmentStrings((LPCTSTR)"%HOMEPATH%", (LPTSTR)rc, 1024) == 0) { delete[] rc; return PIDir(); } PIString hp(rc); memset(rc, 0, 1024); if (ExpandEnvironmentStrings((LPCTSTR)"%HOMEDRIVE%", (LPTSTR)rc, 1024) == 0) { delete[] rc; return PIDir(); } PIString hd(rc); hp.replaceAll("\\", PIDir::separator); delete[] rc; //s.prepend(separator); return PIDir(hd + hp); #else # ifndef ESP_PLATFORM rc = getenv("HOME"); if (rc == 0) return PIDir(); return PIDir(rc); # else return PIDir(); # endif #endif } PIDir PIDir::temporary() { char * rc = 0; #ifdef WINDOWS rc = new char[1024]; memset(rc, 0, 1024); int ret = GetTempPath(1024, (LPTSTR)rc); if (ret == 0) { delete[] rc; return PIDir(); } PIString s(rc); s.replaceAll("\\", PIDir::separator); delete[] rc; s.prepend(separator); return PIDir(s); #else rc = tmpnam(0); if (rc == 0) return PIDir(); PIString s(rc); return PIDir(s.left(s.findLast(PIDir::separator))); #endif } PIVector PIDir::allEntries(const PIString &path) { return PIDir(path).allEntries(); } bool PIDir::make(const PIString & path, bool withParents) { PIDir d(path); if (d.isExists()) return true; return d.make(withParents); } bool PIDir::setCurrent(const PIString & path) { #ifdef WINDOWS if (SetCurrentDirectory((LPCTSTR)(path.data())) != 0) return true; #else if (chdir(path.data()) == 0) return true; #endif printf("[PIDir] setCurrent(\"%s\") error: %s\n", path.data(), errorString().data()); return false; } bool PIDir::makeDir(const PIString & path) { #ifdef WINDOWS if (CreateDirectory((LPCTSTR)(path.data()), NULL) != 0) return true; #else if (mkdir(path.data(), 16877) == 0) return true; #endif printf("[PIDir] makeDir(\"%s\") error: %s\n", path.data(), errorString().data()); return false; } bool PIDir::removeDir(const PIString & path) { #ifdef WINDOWS if (RemoveDirectory((LPCTSTR)(path.data())) != 0) return true; #else if (rmdir(path.data()) == 0) return true; #endif printf("[PIDir] removeDir(\"%s\") error: %s\n", path.data(), errorString().data()); return false; } bool PIDir::renameDir(const PIString & path, const PIString & new_name) { if (::rename(path.data(), new_name.data()) == 0) return true; printf("[PIDir] renameDir(\"%s\", \"%s\") error: %s\n", path.data(), new_name.data(), errorString().data()); return false; }