471 lines
10 KiB
C++
Executable File
471 lines
10 KiB
C++
Executable File
/*
|
|
PIP - Platform Independent Primitives
|
|
Directory
|
|
Copyright (C) 2017 Ivan Pelipenko peri4ko@yandex.ru
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU 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 General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "piincludes_p.h"
|
|
#include "pidir.h"
|
|
|
|
//#if !defined(ANDROID)
|
|
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 <dirent.h>
|
|
# else
|
|
# include <sys/dir.h>
|
|
# endif
|
|
# include <sys/stat.h>
|
|
#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;
|
|
/*#ifdef WINDOWS
|
|
if (path_.size_s() < 2) return false;
|
|
return (path_.mid(1, 2).contains(":"));
|
|
#else
|
|
return (path_[0] == separator);
|
|
#endif*/
|
|
return (path_[0] == separator);
|
|
}
|
|
|
|
|
|
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.size() == 0) {
|
|
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;
|
|
//bool is_abs = isAbsolute();
|
|
if (withParents) {
|
|
PIStringList l = d.path().split(separator);
|
|
//piCout << l;
|
|
l.removeAll("");
|
|
//piCout << l;
|
|
PIString cdp;
|
|
piForeachC (PIString & i, l) {
|
|
if (!cdp.isEmpty())
|
|
cdp += separator;
|
|
cdp += i;
|
|
//piCout << 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<PIFile::FileInfo> PIDir::entries() {
|
|
PIVector<PIFile::FileInfo> 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
|
|
# ifdef QNX
|
|
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) || defined(QNX)
|
|
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<PIFile::FileInfo> PIDir::allEntries() {
|
|
PIVector<PIFile::FileInfo> ret;
|
|
PIVector<PIFile::FileInfo> dirs;
|
|
PIStringList cdirs, ndirs;
|
|
cdirs << path();
|
|
while (!cdirs.isEmpty()) {
|
|
piForeachC (PIString & d, cdirs) {
|
|
scan_ = d;
|
|
PIVector<PIFile::FileInfo> 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() {
|
|
char rc[1024];
|
|
#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
|
|
if (getcwd(rc, 1024) == 0) return PIString();
|
|
#endif
|
|
return PIDir(rc);
|
|
}
|
|
|
|
|
|
PIDir PIDir::home() {
|
|
char * rc = 0;
|
|
#ifdef WINDOWS
|
|
rc = new char[1024];
|
|
memset(rc, 0, 1024);
|
|
if (ExpandEnvironmentStrings((LPCTSTR)"%HOMEPATH%", (LPTSTR)rc, 1024) == 0) {
|
|
delete[] rc;
|
|
return PIDir();
|
|
}
|
|
PIString s(rc);
|
|
s.replaceAll("\\", PIDir::separator);
|
|
delete[] rc;
|
|
s.prepend(separator);
|
|
return PIDir(s);
|
|
#else
|
|
rc = getenv("HOME");
|
|
if (rc == 0) return PIDir();
|
|
return PIDir(rc);
|
|
#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<PIFile::FileInfo> 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;
|
|
}
|
|
|
|
|
|
//#endif
|