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 <stdlib.h>
#include <getopt.h>
#include <iostream>
#include <map>
using namespace std;
static const char *thisprog;
static int op_flags;
#define OPT_MOINS 0x1
#define OPT_r 0x2
#define OPT_s 0x4
static char usage [] =
"pathut\n"
;
static void
Usage(void)
static std::map<std::string, int> options {
{"path_home", 0},
{"path_tildexpand", 0},
{"listdir", 0},
};
static const char *thisprog;
static 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);
}
int main(int argc, const char **argv)
int main(int argc, char **argv)
{
thisprog = argv[0];
argc--; argv++;
thisprog = *argv;
std::vector<struct option> long_options;
while (argc > 0 && **argv == '-') {
(*argv)++;
if (!(**argv))
/* Cas du "adb - core" */
Usage();
while (**argv)
switch (*(*argv)++) {
default: Usage(); break;
}
b1: argc--; argv++;
for (auto& entry : options) {
struct option opt;
opt.name = entry.first.c_str();
opt.has_arg = 0;
opt.flag = &entry.second;
opt.val = 1;
long_options.push_back(opt);
}
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();
cout << "path_home() -> [" << path_home() << "]\n";
cout << "path_tildexpand(~) -> [" << path_tildexpand("~") << "]\n";
}
return 0;
}

View File

@ -19,17 +19,10 @@
#include <stdio.h>
#ifdef _MSC_VER
#include "msvc_dirent.h"
#else // !_MSC_VER
#include <dirent.h>
#endif // _MSC_VER
#include <errno.h>
#include <fnmatch.h>
#include <cstring>
#include <algorithm>
#include <sstream>
#include <vector>
#include <deque>
@ -39,7 +32,6 @@
#include "log.h"
#include "pathut.h"
#include "fstreewalk.h"
#include "transcode.h"
using namespace std;
@ -329,20 +321,6 @@ FsTreeWalker::Status FsTreeWalker::walk(const string& _top, FsTreeWalkerCB& cb)
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.
// This means that we always go into the top 'walk()' parameter if it is a
// directory, even if norecurse is set. Bug or Feature ?
@ -391,20 +369,9 @@ FsTreeWalker::Status FsTreeWalker::iwalk(const string &top,
}
#endif
SYSPATH(top, systop);
DIRHDL *d = OPENDIR(systop);
if (nullptr == d) {
PathDirContents dc(top);
if (!dc.opendir()) {
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) {
case EPERM:
case EACCES:
@ -421,25 +388,20 @@ FsTreeWalker::Status FsTreeWalker::iwalk(const string &top,
}
}
struct DIRENT *ent;
while (errno = 0, ((ent = READDIR(d)) != 0)) {
const struct PathDirContents::Entry *ent;
while (errno = 0, ((ent = dc.readdir()) != nullptr)) {
string fn;
struct PathStat st;
#ifdef _WIN32
string sdname;
if (!wchartoutf8(ent->d_name, sdname)) {
LOGERR("wchartoutf8 failed in " << top << endl);
const string& dname{ent->d_name};
if (dname.empty()) {
// ???
continue;
}
const char *dname = sdname.c_str();
#else
const char *dname = ent->d_name;
#endif
// Maybe skip dotfiles
if ((data->options & FtwSkipDotFiles) && dname[0] == '.')
continue;
// Skip . and ..
if (!strcmp(dname, ".") || !strcmp(dname, ".."))
if (dname == "." || dname == "..")
continue;
// Skipped file names match ?
@ -530,8 +492,6 @@ FsTreeWalker::Status FsTreeWalker::iwalk(const string &top,
}
out:
if (d)
CLOSEDIR(d);
return status;
}

View File

@ -68,9 +68,15 @@
#include <vector>
#include <fcntl.h>
#ifndef PRETEND_USE
#define PRETEND_USE(expr) ((void)(expr))
#endif
// Listing directories: we include the normal dirent.h on Unix-derived
// systems, and on MinGW, where it comes with a supplemental wide char
// 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
@ -115,14 +121,17 @@
#define LSTAT _wstati64
#define STATBUF _stati64
#define ACCESS _waccess
#define OPENDIR _wopendir
#define OPENDIR ::_wopendir
#define DIRHDL _WDIR
#define CLOSEDIR _wclosedir
#define READDIR _wreaddir
#define READDIR ::_wreaddir
#define REWINDDIR ::_wrewinddir
#define DIRENT _wdirent
#define DIRHDL _WDIR
#define MKDIR(a,b) _wmkdir(a)
#define OPEN ::_wopen
#define UNLINK _wunlink
#define RMDIR _wrmdir
#define CHDIR _wchdir
#define ftruncate _chsize_s
@ -149,25 +158,22 @@
#define LSTAT lstat
#define STATBUF stat
#define ACCESS access
#define OPENDIR opendir
#define OPENDIR ::opendir
#define DIRHDL DIR
#define CLOSEDIR closedir
#define READDIR readdir
#define READDIR ::readdir
#define REWINDDIR ::rewinddir
#define DIRENT dirent
#define DIRHDL DIR
#define MKDIR(a,b) mkdir(a,b)
#define O_BINARY 0
#define OPEN ::open
#define UNLINK ::unlink
#define RMDIR ::rmdir
#define CHDIR ::chdir
#endif /* !_WIN32 */
#ifdef _MSC_VER
#include <msvc_dirent.h>
#else // !_MSC_VER
#include <dirent.h>
#endif // _MSC_VER
using namespace std;
#ifndef PATHUT_SSIZE_T
@ -645,28 +651,6 @@ string path_home()
#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)
{
if (s.empty() || s[0] != '~') {
@ -889,6 +873,12 @@ bool path_unlink(const std::string& path)
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__)
// Not sure what g++ version supports fstream assignment but 4.9
// (jessie) certainly does not
@ -1032,6 +1022,11 @@ bool path_readable(const string& path)
SYSPATH(path, syspath);
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
* 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)
{
struct STATBUF st;
int statret;
ostringstream msg;
DIRHDL *d = 0;
SYSPATH(dir, sysdir);
statret = LSTAT(sysdir, &st);
if (statret == -1) {
msg << "listdir: cant stat " << dir << " errno " << errno;
goto out;
}
if (!S_ISDIR(st.st_mode)) {
PathDirContents dc(dir);
if (!path_isdir(dir)) {
msg << "listdir: " << dir << " not a directory";
goto out;
}
if (ACCESS(sysdir, R_OK) < 0) {
if (!path_access(dir, R_OK)) {
msg << "listdir: no read access to " << dir;
goto out;
}
d = OPENDIR(sysdir);
if (d == 0) {
if (!dc.opendir()) {
msg << "listdir: cant opendir " << dir << ", errno " << errno;
goto out;
}
struct DIRENT *ent;
while ((ent = READDIR(d)) != 0) {
#ifdef _WIN32
string sdname;
if (!wchartoutf8(ent->d_name, sdname)) {
const struct PathDirContents::Entry *ent;
while ((ent = dc.readdir()) != 0) {
if (ent->d_name == "." || ent->d_name == "..") {
continue;
}
const char *dname = sdname.c_str();
#else
const char *dname = ent->d_name;
#endif
if (!strcmp(dname, ".") || !strcmp(dname, "..")) {
continue;
}
entries.insert(dname);
entries.insert(ent->d_name);
}
out:
if (d) {
CLOSEDIR(d);
}
reason = msg.str();
if (reason.empty()) {
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);
/// Get the father directory
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
extern std::string path_home();
/// 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***
/// we return an empty path in this case.
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
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
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
/// 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,
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
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()
#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
extern bool listdir(const std::string& dir, std::string& reason,
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_chdir(const std::string& path);
extern std::string path_cwd();
extern bool path_unlink(const std::string& path);
bool path_chdir(const std::string& path);
std::string path_cwd();
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
* 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
@ -151,20 +182,23 @@ extern bool path_unlink(const std::string& path);
* @param path an utf-8 file path.
* @param mode is an std::fstream mode (ios::in etc.) */
extern std::fstream path_open(const std::string& path, int mode);
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);
extern bool path_open(
const std::string& path, int mode, std::fstream& outstream);
/// 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
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
// 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.
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.
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
extern const std::string& path_pkgdatadir();

View File

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

View File

@ -19,140 +19,72 @@
#include "wipedir.h"
#include <stdio.h>
#include <errno.h>
#include <cstring>
#include <string>
#include "log.h"
#include "pathut.h"
#ifdef _MSC_VER
#include "msvc_dirent.h"
#else // !_MSC_VER
#include <dirent.h>
#endif // _MSC_VER
#ifdef _WIN32
#include "safefcntl.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
# include "safeunistd.h"
#else // Not windows ->
#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
# include <unistd.h>
#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;
SYSPATH(dir, sysdir);
statret = LSTAT(sysdir, &st);
if (statret == -1) {
LOGSYSERR("wipedir", "stat", dir);
return -1;
}
if (!S_ISDIR(st.st_mode)) {
LOGERR("wipedir: " << dir << " not a directory\n");
return -1;
if (!path_isdir(dir)) {
LOGERR("wipedir: " << dir << " not a directory\n");
return -1;
}
if (ACCESS(sysdir, R_OK|W_OK|X_OK) < 0) {
LOGSYSERR("wipedir", "access", dir);
return -1;
if (!path_access(dir, R_OK|W_OK|X_OK)) {
LOGSYSERR("wipedir", "access", dir);
return -1;
}
DIRHDL *d = OPENDIR(sysdir);
if (d == 0) {
LOGSYSERR("wipedir", "opendir", dir);
return -1;
PathDirContents dc(dir);
if (!dc.opendir()) {
LOGSYSERR("wipedir", "opendir", dir);
return -1;
}
int remaining = 0;
struct DIRENT *ent;
while ((ent = READDIR(d)) != 0) {
#ifdef _WIN32
string sdname;
if (!wchartoutf8(ent->d_name, sdname)) {
const struct PathDirContents::Entry *ent;
while ((ent = dc.readdir()) != 0) {
const std::string& dname{ent->d_name};
if (dname == "." || dname == "..")
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);
struct STATBUF st;
int statret = LSTAT(sysfn, &st);
if (statret == -1) {
LOGSYSERR("wipedir", "stat", fn);
goto out;
}
if (S_ISDIR(st.st_mode)) {
if (recurse) {
int rr = wipedir(fn, true, true);
if (rr == -1)
goto out;
else
remaining += rr;
if (path_isdir(fn)) {
if (recurse) {
int rr = wipedir(fn, true, true);
if (rr == -1)
goto out;
else
remaining += rr;
} else {
remaining++;
}
} 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;
if (selfalso && ret == 0) {
if (RMDIR(sysdir) < 0) {
LOGSYSERR("wipedir", "rmdir", dir);
ret = -1;
}
if (!path_rmdir(dir)) {
LOGSYSERR("wipedir", "rmdir", dir);
ret = -1;
}
}
out:
if (d)
CLOSEDIR(d);
return ret;
}