Simplify file-related operations portability by moving more code to the system-independant interface in pathut (esp.: dir reading)

This commit is contained in:
Jean-Francois Dockes 2020-09-29 09:48:34 +02:00
parent 3716ea3dac
commit 4b928ee57e
8 changed files with 329 additions and 291 deletions

View File

@ -2,48 +2,88 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <getopt.h>
#include <iostream> #include <iostream>
#include <map>
using namespace std; using namespace std;
static const char *thisprog;
static int op_flags; static std::map<std::string, int> options {
#define OPT_MOINS 0x1 {"path_home", 0},
#define OPT_r 0x2 {"path_tildexpand", 0},
#define OPT_s 0x4 {"listdir", 0},
static char usage [] = };
"pathut\n"
; static const char *thisprog;
static void static void Usage(void)
Usage(void)
{ {
fprintf(stderr, "%s: usage:\n%s", thisprog, usage); string sopts;
for (const auto& opt: options) {
sopts += "--" + opt.first + "\n";
}
fprintf(stderr, "%s: usage: %s\n%s", thisprog, thisprog, sopts.c_str());
exit(1); exit(1);
} }
int main(int argc, const char **argv) int main(int argc, char **argv)
{ {
thisprog = argv[0]; thisprog = *argv;
argc--; argv++; std::vector<struct option> long_options;
while (argc > 0 && **argv == '-') { for (auto& entry : options) {
(*argv)++; struct option opt;
if (!(**argv)) opt.name = entry.first.c_str();
/* Cas du "adb - core" */ opt.has_arg = 0;
Usage(); opt.flag = &entry.second;
while (**argv) opt.val = 1;
switch (*(*argv)++) { long_options.push_back(opt);
default: Usage(); break;
}
b1: argc--; argv++;
} }
long_options.push_back({0, 0, 0, 0});
if (argc != 0) while (getopt_long(argc, argv, "", &long_options[0], nullptr) != -1) {
}
if (options["path_home"]) {
if (optind != argc) {
cerr << "Usage: trsmallut --path_home\n";
return 1;
}
cout << "path_home() -> [" << path_home() << "]\n";
} else if (options["path_tildexpand"]) {
if (optind >= argc) {
cerr << "Usage: trsmallut --path_tildexpand <arg>\n";
return 1;
}
string s = argv[optind];
argc--;
if (optind != argc) {
return 1;
}
cout << "path_tildexpand(" << s << ") -> [" << path_tildexpand(s) <<
"]\n";
} else if (options["listdir"]) {
if (optind >= argc) {
cerr << "Usage: trsmallut --listdir <arg>\n";
return 1;
}
std::string path = argv[optind];
argc--;
if (optind != argc) {
cerr << "Usage: trsmallut --listdir <arg>\n";
return 1;
}
std::string reason;
std::set<std::string> entries;
if (!listdir(path, reason, entries)) {
std::cerr<< "listdir(" << path << ") failed : " << reason << "\n";
return 1;
}
for (const auto& entry : entries) {
cout << entry << "\n";
}
} else {
Usage(); Usage();
}
cout << "path_home() -> [" << path_home() << "]\n";
cout << "path_tildexpand(~) -> [" << path_tildexpand("~") << "]\n";
return 0; return 0;
} }

View File

@ -19,17 +19,10 @@
#include <stdio.h> #include <stdio.h>
#ifdef _MSC_VER
#include "msvc_dirent.h"
#else // !_MSC_VER
#include <dirent.h>
#endif // _MSC_VER
#include <errno.h> #include <errno.h>
#include <fnmatch.h> #include <fnmatch.h>
#include <cstring> #include <cstring>
#include <algorithm> #include <algorithm>
#include <sstream> #include <sstream>
#include <vector> #include <vector>
#include <deque> #include <deque>
@ -39,7 +32,6 @@
#include "log.h" #include "log.h"
#include "pathut.h" #include "pathut.h"
#include "fstreewalk.h" #include "fstreewalk.h"
#include "transcode.h"
using namespace std; using namespace std;
@ -329,20 +321,6 @@ FsTreeWalker::Status FsTreeWalker::walk(const string& _top, FsTreeWalkerCB& cb)
return FtwOk; return FtwOk;
} }
#ifdef _WIN32
#define DIRENT _wdirent
#define DIRHDL _WDIR
#define OPENDIR _wopendir
#define CLOSEDIR _wclosedir
#define READDIR _wreaddir
#else
#define DIRENT dirent
#define DIRHDL DIR
#define OPENDIR opendir
#define CLOSEDIR closedir
#define READDIR readdir
#endif
// Note that the 'norecurse' flag is handled as part of the directory read. // Note that the 'norecurse' flag is handled as part of the directory read.
// This means that we always go into the top 'walk()' parameter if it is a // This means that we always go into the top 'walk()' parameter if it is a
// directory, even if norecurse is set. Bug or Feature ? // directory, even if norecurse is set. Bug or Feature ?
@ -391,20 +369,9 @@ FsTreeWalker::Status FsTreeWalker::iwalk(const string &top,
} }
#endif #endif
SYSPATH(top, systop); PathDirContents dc(top);
DIRHDL *d = OPENDIR(systop); if (!dc.opendir()) {
if (nullptr == d) {
data->logsyserr("opendir", top); data->logsyserr("opendir", top);
#ifdef _WIN32
int rc = GetLastError();
LOGERR("opendir failed: LastError " << rc << endl);
if (rc == ERROR_NETNAME_DELETED) {
// 64: share disconnected.
// Not too sure of the errno in this case.
// Make sure it's not one of the permissible ones
errno = ENODEV;
}
#endif
switch (errno) { switch (errno) {
case EPERM: case EPERM:
case EACCES: case EACCES:
@ -421,25 +388,20 @@ FsTreeWalker::Status FsTreeWalker::iwalk(const string &top,
} }
} }
struct DIRENT *ent; const struct PathDirContents::Entry *ent;
while (errno = 0, ((ent = READDIR(d)) != 0)) { while (errno = 0, ((ent = dc.readdir()) != nullptr)) {
string fn; string fn;
struct PathStat st; struct PathStat st;
#ifdef _WIN32 const string& dname{ent->d_name};
string sdname; if (dname.empty()) {
if (!wchartoutf8(ent->d_name, sdname)) { // ???
LOGERR("wchartoutf8 failed in " << top << endl);
continue; continue;
} }
const char *dname = sdname.c_str();
#else
const char *dname = ent->d_name;
#endif
// Maybe skip dotfiles // Maybe skip dotfiles
if ((data->options & FtwSkipDotFiles) && dname[0] == '.') if ((data->options & FtwSkipDotFiles) && dname[0] == '.')
continue; continue;
// Skip . and .. // Skip . and ..
if (!strcmp(dname, ".") || !strcmp(dname, "..")) if (dname == "." || dname == "..")
continue; continue;
// Skipped file names match ? // Skipped file names match ?
@ -530,8 +492,6 @@ FsTreeWalker::Status FsTreeWalker::iwalk(const string &top,
} }
out: out:
if (d)
CLOSEDIR(d);
return status; return status;
} }

View File

@ -68,9 +68,15 @@
#include <vector> #include <vector>
#include <fcntl.h> #include <fcntl.h>
#ifndef PRETEND_USE // Listing directories: we include the normal dirent.h on Unix-derived
#define PRETEND_USE(expr) ((void)(expr)) // systems, and on MinGW, where it comes with a supplemental wide char
#endif // interface. When building with MSVC, we use our bundled msvc_dirent.h,
// which is equivalent to the one in MinGW
#ifdef _MSC_VER
#include "msvc_dirent.h"
#else // !_MSC_VER
#include <dirent.h>
#endif // _MSC_VER
#ifdef _WIN32 #ifdef _WIN32
@ -115,14 +121,17 @@
#define LSTAT _wstati64 #define LSTAT _wstati64
#define STATBUF _stati64 #define STATBUF _stati64
#define ACCESS _waccess #define ACCESS _waccess
#define OPENDIR _wopendir #define OPENDIR ::_wopendir
#define DIRHDL _WDIR
#define CLOSEDIR _wclosedir #define CLOSEDIR _wclosedir
#define READDIR _wreaddir #define READDIR ::_wreaddir
#define REWINDDIR ::_wrewinddir
#define DIRENT _wdirent #define DIRENT _wdirent
#define DIRHDL _WDIR #define DIRHDL _WDIR
#define MKDIR(a,b) _wmkdir(a) #define MKDIR(a,b) _wmkdir(a)
#define OPEN ::_wopen #define OPEN ::_wopen
#define UNLINK _wunlink #define UNLINK _wunlink
#define RMDIR _wrmdir
#define CHDIR _wchdir #define CHDIR _wchdir
#define ftruncate _chsize_s #define ftruncate _chsize_s
@ -149,25 +158,22 @@
#define LSTAT lstat #define LSTAT lstat
#define STATBUF stat #define STATBUF stat
#define ACCESS access #define ACCESS access
#define OPENDIR opendir #define OPENDIR ::opendir
#define DIRHDL DIR
#define CLOSEDIR closedir #define CLOSEDIR closedir
#define READDIR readdir #define READDIR ::readdir
#define REWINDDIR ::rewinddir
#define DIRENT dirent #define DIRENT dirent
#define DIRHDL DIR #define DIRHDL DIR
#define MKDIR(a,b) mkdir(a,b) #define MKDIR(a,b) mkdir(a,b)
#define O_BINARY 0 #define O_BINARY 0
#define OPEN ::open #define OPEN ::open
#define UNLINK ::unlink #define UNLINK ::unlink
#define RMDIR ::rmdir
#define CHDIR ::chdir #define CHDIR ::chdir
#endif /* !_WIN32 */ #endif /* !_WIN32 */
#ifdef _MSC_VER
#include <msvc_dirent.h>
#else // !_MSC_VER
#include <dirent.h>
#endif // _MSC_VER
using namespace std; using namespace std;
#ifndef PATHUT_SSIZE_T #ifndef PATHUT_SSIZE_T
@ -645,28 +651,6 @@ string path_home()
#endif #endif
} }
// The default place to store the default config and other stuff (e.g webqueue)
string path_homedata()
{
#ifdef _WIN32
wchar_t *cp;
SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &cp);
string dir;
if (cp != 0) {
wchartoutf8(cp, dir);
}
if (!dir.empty()) {
dir = path_canon(dir);
} else {
dir = path_cat(path_home(), "AppData/Local/");
}
return dir;
#else
// We should use an xdg-conforming location, but, history...
return path_home();
#endif
}
string path_tildexpand(const string& s) string path_tildexpand(const string& s)
{ {
if (s.empty() || s[0] != '~') { if (s.empty() || s[0] != '~') {
@ -889,6 +873,12 @@ bool path_unlink(const std::string& path)
return UNLINK(syspath) == 0; return UNLINK(syspath) == 0;
} }
bool path_rmdir(const std::string& path)
{
SYSPATH(path, syspath);
return RMDIR(syspath) == 0;
}
#if !defined(__GNUC__) || __GNUC__ > 4 || defined(__clang__) #if !defined(__GNUC__) || __GNUC__ > 4 || defined(__clang__)
// Not sure what g++ version supports fstream assignment but 4.9 // Not sure what g++ version supports fstream assignment but 4.9
// (jessie) certainly does not // (jessie) certainly does not
@ -1032,6 +1022,11 @@ bool path_readable(const string& path)
SYSPATH(path, syspath); SYSPATH(path, syspath);
return ACCESS(syspath, R_OK) == 0; return ACCESS(syspath, R_OK) == 0;
} }
bool path_access(const std::string& path, int mode)
{
SYSPATH(path, syspath);
return ACCESS(syspath, mode) == 0;
}
/* There is a lot of vagueness about what should be percent-encoded or /* There is a lot of vagueness about what should be percent-encoded or
* not in a file:// url. The constraint that we have is that we may use * not in a file:// url. The constraint that we have is that we may use
@ -1296,56 +1291,105 @@ ParsedUri::ParsedUri(std::string uri)
} }
} }
/// Directory reading interface. UTF-8 on Windows.
class PathDirContents::Internal {
public:
~Internal() {
if (dirhdl) {
CLOSEDIR(dirhdl);
}
}
DIRHDL *dirhdl{nullptr};
PathDirContents::Entry entry;
std::string dirpath;
};
PathDirContents::PathDirContents(const std::string& dirpath)
{
m = new Internal;
m->dirpath = dirpath;
}
PathDirContents::~PathDirContents()
{
delete m;
}
bool PathDirContents::opendir()
{
if (m->dirhdl) {
CLOSEDIR(m->dirhdl);
m->dirhdl = nullptr;
}
SYSPATH(m->dirpath, sysdir);
m->dirhdl = OPENDIR(sysdir);
#ifdef _WIN32
int rc = GetLastError();
LOGERR("opendir failed: LastError " << rc << endl);
if (rc == ERROR_NETNAME_DELETED) {
// 64: share disconnected.
// Not too sure of the errno in this case.
// Make sure it's not one of the permissible ones
errno = ENODEV;
}
#endif
return ! (nullptr == m->dirhdl);
}
void PathDirContents::rewinddir()
{
REWINDDIR(m->dirhdl);
}
const struct PathDirContents::Entry* PathDirContents::readdir()
{
struct DIRENT *ent = READDIR(m->dirhdl);
if (nullptr == ent) {
return nullptr;
}
#ifdef _WIN32
string sdname;
if (!wchartoutf8(ent->d_name, sdname)) {
LOGERR("wchartoutf8 failed for " << ent->d_name << endl);
return nullptr;
}
const char *dname = sdname.c_str();
#else
const char *dname = ent->d_name;
#endif
m->entry.d_name = dname;
return &m->entry;
}
bool listdir(const string& dir, string& reason, set<string>& entries) bool listdir(const string& dir, string& reason, set<string>& entries)
{ {
struct STATBUF st;
int statret;
ostringstream msg; ostringstream msg;
DIRHDL *d = 0; PathDirContents dc(dir);
SYSPATH(dir, sysdir); if (!path_isdir(dir)) {
statret = LSTAT(sysdir, &st);
if (statret == -1) {
msg << "listdir: cant stat " << dir << " errno " << errno;
goto out;
}
if (!S_ISDIR(st.st_mode)) {
msg << "listdir: " << dir << " not a directory"; msg << "listdir: " << dir << " not a directory";
goto out; goto out;
} }
if (ACCESS(sysdir, R_OK) < 0) { if (!path_access(dir, R_OK)) {
msg << "listdir: no read access to " << dir; msg << "listdir: no read access to " << dir;
goto out; goto out;
} }
d = OPENDIR(sysdir); if (!dc.opendir()) {
if (d == 0) {
msg << "listdir: cant opendir " << dir << ", errno " << errno; msg << "listdir: cant opendir " << dir << ", errno " << errno;
goto out; goto out;
} }
const struct PathDirContents::Entry *ent;
struct DIRENT *ent; while ((ent = dc.readdir()) != 0) {
while ((ent = READDIR(d)) != 0) { if (ent->d_name == "." || ent->d_name == "..") {
#ifdef _WIN32
string sdname;
if (!wchartoutf8(ent->d_name, sdname)) {
continue; continue;
} }
const char *dname = sdname.c_str(); entries.insert(ent->d_name);
#else
const char *dname = ent->d_name;
#endif
if (!strcmp(dname, ".") || !strcmp(dname, "..")) {
continue;
}
entries.insert(dname);
} }
out: out:
if (d) {
CLOSEDIR(d);
}
reason = msg.str(); reason = msg.str();
if (reason.empty()) { if (reason.empty()) {
return true; return true;

View File

@ -39,6 +39,21 @@ extern std::string path_basename(const std::string& s,
extern std::string path_suffix(const std::string& s); extern std::string path_suffix(const std::string& s);
/// Get the father directory /// Get the father directory
extern std::string path_getfather(const std::string& s); extern std::string path_getfather(const std::string& s);
/// Test if path is absolute
extern bool path_isabsolute(const std::string& s);
/// Test if path is root (x:/). root is defined by root/.. == root
extern bool path_isroot(const std::string& p);
/// Test if sub is a subdirectory of top. This is a textual test,
/// links not allowed
extern bool path_isdesc(const std::string& top, const std::string& sub);
/// Clean up path by removing duplicated / and resolving ../ + make it absolute
extern std::string path_canon(const std::string& s, const std::string *cwd = 0);
/// Check that path refers to same file. Uses dev/ino on Linux,
/// textual comparison on Windows.
bool path_samefile(const std::string& p1, const std::string& p2);
/// Get the current user's home directory /// Get the current user's home directory
extern std::string path_home(); extern std::string path_home();
/// Expand ~ at the beginning of std::string /// Expand ~ at the beginning of std::string
@ -46,26 +61,6 @@ extern std::string path_tildexpand(const std::string& s);
/// Use getcwd() to make absolute path if needed. Beware: ***this can fail*** /// Use getcwd() to make absolute path if needed. Beware: ***this can fail***
/// we return an empty path in this case. /// we return an empty path in this case.
extern std::string path_absolute(const std::string& s); extern std::string path_absolute(const std::string& s);
/// Clean up path by removing duplicated / and resolving ../ + make it absolute
extern std::string path_canon(const std::string& s, const std::string *cwd = 0);
/// Use glob(3) to return the file names matching pattern inside dir
extern std::vector<std::string> path_dirglob(const std::string& dir,
const std::string pattern);
/// Encode according to rfc 1738
extern std::string url_encode(const std::string& url,
std::string::size_type offs = 0);
extern std::string url_decode(const std::string& encoded);
//// Convert to file path if url is like file://. This modifies the
//// input (and returns a copy for convenience)
extern std::string fileurltolocalpath(std::string url);
/// Test for file:/// url
extern bool urlisfileurl(const std::string& url);
///
extern std::string url_parentfolder(const std::string& url);
/// Return the host+path part of an url. This is not a general
/// routine, it does the right thing only in the recoll context
extern std::string url_gpath(const std::string& url);
/// Stat parameter and check if it's a directory /// Stat parameter and check if it's a directory
extern bool path_isdir(const std::string& path, bool follow = false); extern bool path_isdir(const std::string& path, bool follow = false);
@ -75,7 +70,30 @@ extern bool path_isfile(const std::string& path, bool follow = false);
/// Retrieve file size /// Retrieve file size
extern long long path_filesize(const std::string& path); extern long long path_filesize(const std::string& path);
bool path_samefile(const std::string& p1, const std::string& p2); /// Check that path is traversable and last element exists
/// Returns true if last elt could be checked to exist. False may mean that
/// the file/dir does not exist or that an error occurred.
bool path_exists(const std::string& path);
/// Same but must be readable
bool path_readable(const std::string& path);
#ifdef _WIN32
# ifndef R_OK
# define R_OK 4
# endif
# ifndef W_OK
# define W_OK 2
# endif
# ifndef X_OK
// Not useful/supported on Windows. Define as R_OK
# define X_OK R_OK
# endif
# ifndef F_OK
# define F_OK 0
# endif
#endif /* _WIN32 */
/// access() or _waccess()
bool path_access(const std::string& path, int mode);
/// Retrieve essential file attributes. This is used rather than a /// Retrieve essential file attributes. This is used rather than a
/// bare stat() to ensure consistent use of the time fields (on /// bare stat() to ensure consistent use of the time fields (on
@ -100,12 +118,6 @@ struct PathStat {
extern int path_fileprops(const std::string path, struct PathStat *stp, extern int path_fileprops(const std::string path, struct PathStat *stp,
bool follow = true); bool follow = true);
/// Check that path is traversable and last element exists
/// Returns true if last elt could be checked to exist. False may mean that
/// the file/dir does not exist or that an error occurred.
extern bool path_exists(const std::string& path);
/// Same but must be readable
extern bool path_readable(const std::string& path);
/// Return separator for PATH environment variable /// Return separator for PATH environment variable
extern std::string path_PATHsep(); extern std::string path_PATHsep();
@ -121,6 +133,24 @@ extern bool utf8towchar(const std::string& in, wchar_t *out, size_t obytescap);
#define SYSPATH(PATH, SPATH) const char *SPATH = PATH.c_str() #define SYSPATH(PATH, SPATH) const char *SPATH = PATH.c_str()
#endif #endif
/// Directory reading interface. UTF-8 on Windows.
class PathDirContents {
public:
PathDirContents(const std::string& dirpath);
~PathDirContents();
bool opendir();
struct Entry {
std::string d_name;
};
const struct Entry* readdir();
void rewinddir();
private:
class Internal;
Internal *m{nullptr};
};
/// Dump directory /// Dump directory
extern bool listdir(const std::string& dir, std::string& reason, extern bool listdir(const std::string& dir, std::string& reason,
std::set<std::string>& entries); std::set<std::string>& entries);
@ -136,10 +166,11 @@ bool fsocc(const std::string& path, int *pc, long long *avmbs = 0);
extern bool path_makepath(const std::string& path, int mode); extern bool path_makepath(const std::string& path, int mode);
/// ///
extern bool path_chdir(const std::string& path); bool path_chdir(const std::string& path);
extern std::string path_cwd(); std::string path_cwd();
extern bool path_unlink(const std::string& path); bool path_unlink(const std::string& path);
bool path_rmdir(const std::string& path);
/* Open file, trying to do the right thing with non-ASCII paths on /* Open file, trying to do the right thing with non-ASCII paths on
* Windows, where it only works with MSVC at the moment if the path is * Windows, where it only works with MSVC at the moment if the path is
* not ASCII, because it uses fstream(wchar_t*), which is an MSVC * not ASCII, because it uses fstream(wchar_t*), which is an MSVC
@ -151,20 +182,23 @@ extern bool path_unlink(const std::string& path);
* @param path an utf-8 file path. * @param path an utf-8 file path.
* @param mode is an std::fstream mode (ios::in etc.) */ * @param mode is an std::fstream mode (ios::in etc.) */
extern std::fstream path_open(const std::string& path, int mode); extern std::fstream path_open(const std::string& path, int mode);
extern bool path_open(const std::string& path, int mode, std::fstream& outstream); extern bool path_open(
const std::string& path, int mode, std::fstream& outstream);
/// Where we create the user data subdirs
extern std::string path_homedata();
/// Test if path is absolute
extern bool path_isabsolute(const std::string& s);
/// Test if path is root (x:/). root is defined by root/.. == root
extern bool path_isroot(const std::string& p);
/// Test if sub is a subdirectory of top. This is a textual test,
/// links not allowed
extern bool path_isdesc(const std::string& top, const std::string& sub);
/// Encode according to rfc 1738
extern std::string url_encode(const std::string& url,
std::string::size_type offs = 0);
extern std::string url_decode(const std::string& encoded);
//// Convert to file path if url is like file://. This modifies the
//// input (and returns a copy for convenience)
extern std::string fileurltolocalpath(std::string url);
/// Test for file:/// url
extern bool urlisfileurl(const std::string& url);
///
extern std::string url_parentfolder(const std::string& url);
/// Return the host+path part of an url. This is not a general
/// routine, it does the right thing only in the recoll context
extern std::string url_gpath(const std::string& url);
/// Turn absolute path into file:// url /// Turn absolute path into file:// url
extern std::string path_pathtofileurl(const std::string& path); extern std::string path_pathtofileurl(const std::string& path);

View File

@ -194,6 +194,28 @@ static bool path_gettempfilename(string& filename, string& reason)
} }
#endif // posix #endif // posix
// The default place to store the default config and other stuff (e.g webqueue)
string path_homedata()
{
#ifdef _WIN32
wchar_t *cp;
SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &cp);
string dir;
if (cp != 0) {
wchartoutf8(cp, dir);
}
if (!dir.empty()) {
dir = path_canon(dir);
} else {
dir = path_cat(path_home(), "AppData/Local/");
}
return dir;
#else
// We should use an xdg-conforming location, but, history...
return path_home();
#endif
}
// Check if path is either non-existing or an empty directory. // Check if path is either non-existing or an empty directory.
bool path_empty(const string& path) bool path_empty(const string& path)
{ {

View File

@ -33,6 +33,9 @@ extern std::string path_defaultrecollconfsubdir();
// Check if path is either non-existing or an empty directory. // Check if path is either non-existing or an empty directory.
extern bool path_empty(const std::string& path); extern bool path_empty(const std::string& path);
/// Where we create the user data subdirs
extern std::string path_homedata();
/// e.g. /usr/share/recoll. Depends on OS and config /// e.g. /usr/share/recoll. Depends on OS and config
extern const std::string& path_pkgdatadir(); extern const std::string& path_pkgdatadir();

View File

@ -41,6 +41,9 @@ void smallut_init_mt();
#ifndef deleteZ #ifndef deleteZ
#define deleteZ(X) {delete X;X = 0;} #define deleteZ(X) {delete X;X = 0;}
#endif #endif
#ifndef PRETEND_USE
#define PRETEND_USE(var) ((void)(var))
#endif
#endif /* SMALLUT_DISABLE_MACROS */ #endif /* SMALLUT_DISABLE_MACROS */
// Case-insensitive compare. ASCII ONLY ! // Case-insensitive compare. ASCII ONLY !

View File

@ -19,140 +19,72 @@
#include "wipedir.h" #include "wipedir.h"
#include <stdio.h>
#include <errno.h>
#include <cstring>
#include <string> #include <string>
#include "log.h" #include "log.h"
#include "pathut.h" #include "pathut.h"
#ifdef _MSC_VER
#include "msvc_dirent.h"
#else // !_MSC_VER
#include <dirent.h>
#endif // _MSC_VER
#ifdef _WIN32 #ifdef _WIN32
#include "safefcntl.h" # include "safeunistd.h"
#include "safeunistd.h"
#include "safewindows.h"
#include "safesysstat.h"
#include "transcode.h"
#define STAT _wstati64
#define LSTAT _wstati64
#define STATBUF _stati64
#define ACCESS _waccess
#define OPENDIR _wopendir
#define CLOSEDIR _wclosedir
#define READDIR _wreaddir
#define DIRENT _wdirent
#define DIRHDL _WDIR
#define UNLINK _wunlink
#define RMDIR _wrmdir
#else // Not windows -> #else // Not windows ->
# include <unistd.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#define STAT stat
#define LSTAT lstat
#define STATBUF stat
#define ACCESS access
#define OPENDIR opendir
#define CLOSEDIR closedir
#define READDIR readdir
#define DIRENT dirent
#define DIRHDL DIR
#define UNLINK unlink
#define RMDIR rmdir
#endif #endif
using namespace std;
int wipedir(const string& dir, bool selfalso, bool recurse) int wipedir(const std::string& dir, bool selfalso, bool recurse)
{ {
struct STATBUF st;
int statret;
int ret = -1; int ret = -1;
SYSPATH(dir, sysdir); if (!path_isdir(dir)) {
statret = LSTAT(sysdir, &st); LOGERR("wipedir: " << dir << " not a directory\n");
if (statret == -1) { return -1;
LOGSYSERR("wipedir", "stat", dir);
return -1;
}
if (!S_ISDIR(st.st_mode)) {
LOGERR("wipedir: " << dir << " not a directory\n");
return -1;
} }
if (ACCESS(sysdir, R_OK|W_OK|X_OK) < 0) { if (!path_access(dir, R_OK|W_OK|X_OK)) {
LOGSYSERR("wipedir", "access", dir); LOGSYSERR("wipedir", "access", dir);
return -1; return -1;
} }
DIRHDL *d = OPENDIR(sysdir); PathDirContents dc(dir);
if (d == 0) { if (!dc.opendir()) {
LOGSYSERR("wipedir", "opendir", dir); LOGSYSERR("wipedir", "opendir", dir);
return -1; return -1;
} }
int remaining = 0; int remaining = 0;
struct DIRENT *ent; const struct PathDirContents::Entry *ent;
while ((ent = READDIR(d)) != 0) { while ((ent = dc.readdir()) != 0) {
#ifdef _WIN32 const std::string& dname{ent->d_name};
string sdname; if (dname == "." || dname == "..")
if (!wchartoutf8(ent->d_name, sdname)) {
continue; continue;
}
const char *dname = sdname.c_str();
#else
const char *dname = ent->d_name;
#endif
if (!strcmp(dname, ".") || !strcmp(dname, ".."))
continue;
string fn = path_cat(dir, dname); std::string fn = path_cat(dir, dname);
SYSPATH(fn, sysfn); if (path_isdir(fn)) {
struct STATBUF st; if (recurse) {
int statret = LSTAT(sysfn, &st); int rr = wipedir(fn, true, true);
if (statret == -1) { if (rr == -1)
LOGSYSERR("wipedir", "stat", fn); goto out;
goto out; else
} remaining += rr;
if (S_ISDIR(st.st_mode)) { } else {
if (recurse) { remaining++;
int rr = wipedir(fn, true, true); }
if (rr == -1)
goto out;
else
remaining += rr;
} else { } else {
remaining++; if (!path_unlink(fn)) {
LOGSYSERR("wipedir", "unlink", fn);
goto out;
}
} }
} else {
if (UNLINK(sysfn) < 0) {
LOGSYSERR("wipedir", "unlink", fn);
goto out;
}
}
} }
ret = remaining; ret = remaining;
if (selfalso && ret == 0) { if (selfalso && ret == 0) {
if (RMDIR(sysdir) < 0) { if (!path_rmdir(dir)) {
LOGSYSERR("wipedir", "rmdir", dir); LOGSYSERR("wipedir", "rmdir", dir);
ret = -1; ret = -1;
} }
} }
out: out:
if (d)
CLOSEDIR(d);
return ret; return ret;
} }