From 6aeef686fc288f71251a017434b8dc2fd31b2ece Mon Sep 17 00:00:00 2001 From: "\"Jean-Francois Dockes ext:(%22)" Date: Sat, 24 Jul 2010 12:08:06 +0200 Subject: [PATCH] Created class to simplify temp directory management --- src/index/beaglequeue.cpp | 16 -------- src/index/beaglequeue.h | 2 +- src/index/fsindexer.cpp | 16 -------- src/index/fsindexer.h | 2 +- src/internfile/internfile.cpp | 55 +++++++++---------------- src/internfile/internfile.h | 15 ++++--- src/kde/kioslave/recoll/htmlif.cpp | 13 ++---- src/qtgui/preview_w.cpp | 10 ++--- src/query/recollq.cpp | 28 ++++--------- src/utils/Makefile | 2 +- src/utils/pathut.cpp | 61 ++++++++++++++++++++-------- src/utils/pathut.h | 14 +++++++ src/utils/transcode.cpp | 5 +-- src/utils/wipedir.cpp | 65 ++++++++++++++++++++++++++---- src/utils/wipedir.h | 4 +- 15 files changed, 163 insertions(+), 145 deletions(-) diff --git a/src/index/beaglequeue.cpp b/src/index/beaglequeue.cpp index 20819e41..df987b64 100644 --- a/src/index/beaglequeue.cpp +++ b/src/index/beaglequeue.cpp @@ -187,28 +187,12 @@ BeagleQueueIndexer::BeagleQueueIndexer(RclConfig *cnf, Rcl::Db *db, if (!m_config->getConfParam("beaglequeuedir", m_queuedir)) m_queuedir = path_tildexpand("~/.beagle/ToIndex/"); path_catslash(m_queuedir); - - if (m_db && (m_tmpdir.empty() || access(m_tmpdir.c_str(), 0) < 0)) { - string reason; - if (!maketmpdir(m_tmpdir, reason)) { - LOGERR(("DbIndexer: cannot create temporary directory: %s\n", - reason.c_str())); - m_tmpdir = badtmpdirname; - } - } m_cache = new BeagleQueueCache(cnf); } BeagleQueueIndexer::~BeagleQueueIndexer() { LOGDEB(("BeagleQueueIndexer::~\n")); - if (m_tmpdir.length() && m_tmpdir.compare(badtmpdirname)) { - wipedir(m_tmpdir); - if (rmdir(m_tmpdir.c_str()) < 0) { - LOGERR(("BeagleQueueIndexer::~: cannot clear temp dir %s\n", - m_tmpdir.c_str())); - } - } deleteZ(m_cache); } diff --git a/src/index/beaglequeue.h b/src/index/beaglequeue.h index 624eb370..7b6300b6 100644 --- a/src/index/beaglequeue.h +++ b/src/index/beaglequeue.h @@ -69,7 +69,7 @@ private: Rcl::Db *m_db; BeagleQueueCache *m_cache; string m_queuedir; - string m_tmpdir; + TempDir m_tmpdir; DbIxStatusUpdater *m_updater; bool m_nocacheindex; diff --git a/src/index/fsindexer.cpp b/src/index/fsindexer.cpp index 5f99eb82..e771a97d 100644 --- a/src/index/fsindexer.cpp +++ b/src/index/fsindexer.cpp @@ -67,26 +67,10 @@ using namespace std; #endif FsIndexer::~FsIndexer() { - // Maybe clean up temporary directory - if (!m_tmpdir.empty()) { - wipedir(m_tmpdir); - if (rmdir(m_tmpdir.c_str()) < 0) { - LOGERR(("FsIndexer::~FsIndexer: cannot clear temp dir %s\n", - m_tmpdir.c_str())); - } - } } bool FsIndexer::init() { - if (m_tmpdir.empty() || access(m_tmpdir.c_str(), 0) < 0) { - if (!maketmpdir(m_tmpdir, m_reason)) { - LOGERR(("FsIndexer: cannot create temporary directory: %s\n", - m_reason.c_str())); - m_tmpdir.erase(); - return false; - } - } if (m_tdl.empty()) { m_tdl = m_config->getTopdirs(); if (m_tdl.empty()) { diff --git a/src/index/fsindexer.h b/src/index/fsindexer.h index e0c96cc4..e98fdb13 100644 --- a/src/index/fsindexer.h +++ b/src/index/fsindexer.h @@ -76,7 +76,7 @@ class FsIndexer : public FsTreeWalkerCB { FsTreeWalker m_walker; RclConfig *m_config; Rcl::Db *m_db; - string m_tmpdir; + TempDir m_tmpdir; string m_reason; DbIxStatusUpdater *m_updater; list m_tdl; diff --git a/src/internfile/internfile.cpp b/src/internfile/internfile.cpp index 98fd98d1..4accab9c 100644 --- a/src/internfile/internfile.cpp +++ b/src/internfile/internfile.cpp @@ -41,7 +41,6 @@ using namespace std; #include "mimehandler.h" #include "execmd.h" #include "pathut.h" -#include "wipedir.h" #include "rclconfig.h" #include "mh_html.h" #include "fileudi.h" @@ -111,12 +110,12 @@ bool FileInterner::getEnclosing(const string &url, const string &ipath, // Uncompress input file into a temporary one, by executing the appropriate // script. static bool uncompressfile(RclConfig *conf, const string& ifn, - const list& cmdv, const string& tdir, + const list& cmdv, TempDir& tdir, string& tfile) { // Make sure tmp dir is empty. we guarantee this to filters - if (wipedir(tdir) != 0) { - LOGERR(("uncompressfile: can't clear temp dir %s\n", tdir.c_str())); + if (!tdir.ok() || !tdir.wipe()) { + LOGERR(("uncompressfile: can't clear temp dir %s\n", tdir.dirname())); return false; } string cmd = cmdv.front(); @@ -127,7 +126,7 @@ static bool uncompressfile(RclConfig *conf, const string& ifn, list args; map subs; subs['f'] = ifn; - subs['t'] = tdir; + subs['t'] = tdir.dirname(); for (; it != cmdv.end(); it++) { string ns; pcSubst(*it, ns, subs); @@ -140,7 +139,7 @@ static bool uncompressfile(RclConfig *conf, const string& ifn, if (status || tfile.empty()) { LOGERR(("uncompressfile: doexec: failed for [%s] status 0x%x\n", ifn.c_str(), status)); - if (wipedir(tdir.c_str())) { + if (!tdir.wipe()) { LOGERR(("uncompressfile: wipedir failed\n")); } return false; @@ -153,7 +152,7 @@ static bool uncompressfile(RclConfig *conf, const string& ifn, // Delete temporary uncompressed file void FileInterner::tmpcleanup() { - if (m_tdir.empty() || m_tfile.empty()) + if (m_tfile.empty()) return; if (unlink(m_tfile.c_str()) < 0) { LOGERR(("FileInterner::tmpcleanup: unlink(%s) errno %d\n", @@ -171,15 +170,15 @@ void FileInterner::tmpcleanup() // Split into "constructor calls init()" to allow use from other constructor FileInterner::FileInterner(const string &f, const struct stat *stp, RclConfig *cnf, - const string& td, int flags, const string *imime) + TempDir& td, int flags, const string *imime) : m_tdir(td), m_ok(false) { initcommon(cnf, flags); - init(f, stp, cnf, td, flags, imime); + init(f, stp, cnf, flags, imime); } void FileInterner::init(const string &f, const struct stat *stp, RclConfig *cnf, - const string& td, int flags, const string *imime) + int flags, const string *imime) { m_fn = f; @@ -230,7 +229,7 @@ void FileInterner::init(const string &f, const struct stat *stp, RclConfig *cnf, return; } LOGDEB1(("internfile: after ucomp: m_tdir %s, tfile %s\n", - m_tdir.c_str(), m_tfile.c_str())); + m_tdir.dirname(), m_tfile.c_str())); m_fn = m_tfile; // Note: still using the original file's stat. right ? l_mime = mimetype(m_fn, stp, m_cfg, usfci); @@ -287,15 +286,15 @@ void FileInterner::init(const string &f, const struct stat *stp, RclConfig *cnf, // Setup from memory data (ie: out of the web cache). imime needs to be set. FileInterner::FileInterner(const string &data, RclConfig *cnf, - const string& td, int flags, const string& imime) + TempDir& td, int flags, const string& imime) : m_tdir(td), m_ok(false) { initcommon(cnf, flags); - init(data, cnf, td, flags, imime); + init(data, cnf, flags, imime); } void FileInterner::init(const string &data, RclConfig *cnf, - const string& td, int flags, const string& imime) + int flags, const string& imime) { if (imime.empty()) { LOGERR(("FileInterner: inmemory constructor needs input mime type\n")); @@ -354,7 +353,7 @@ void FileInterner::initcommon(RclConfig *cnf, int flags) } FileInterner::FileInterner(const Rcl::Doc& idoc, RclConfig *cnf, - const string& td, int flags) + TempDir& td, int flags) : m_tdir(td), m_ok(false) { LOGDEB(("FileInterner::FileInterner(idoc)\n")); @@ -390,7 +389,7 @@ FileInterner::FileInterner(const Rcl::Doc& idoc, RclConfig *cnf, fn.c_str())); return; } - init(fn, &st, cnf, td, flags, &idoc.mimetype); + init(fn, &st, cnf, flags, &idoc.mimetype); } else if (!backend.compare("BGL")) { // Retrieve from our webcache (beagle data). The beagler // object is created at the first call of this routine and @@ -414,7 +413,7 @@ FileInterner::FileInterner(const Rcl::Doc& idoc, RclConfig *cnf, LOGINFO(("FileInterner:: udi [%s], mimetp mismatch: in: [%s], bgl " "[%s]\n", idoc.mimetype.c_str(), dotdoc.mimetype.c_str())); } - init(data, cnf, td, flags, dotdoc.mimetype); + init(data, cnf, flags, dotdoc.mimetype); } else { LOGERR(("FileInterner:: unknown backend: [%s]\n", backend.c_str())); return; @@ -868,20 +867,6 @@ FileInterner::Status FileInterner::internfile(Rcl::Doc& doc, string& ipath) return FIAgain; } -// Automatic cleanup of iDocToFile's temp dir -class DirWiper { - public: - string dir; - bool do_it; - DirWiper(string d) : dir(d), do_it(true) {} - ~DirWiper() { - if (do_it) { - wipedir(dir); - rmdir(dir.c_str()); - } - } -}; - // Temporary while we fix backend things static string urltolocalpath(string url) { @@ -894,8 +879,7 @@ static string urltolocalpath(string url) // the input mtype, so that no data conversion is performed. // We then write the data out of the resulting document into the output file. // There are two temporary objects: -// - The internfile temporary directory gets destroyed before we -// return by the DirWiper object +// - The internfile temporary directory gets destroyed by its destructor // - The output temporary file which is held in a reference-counted // object and will be deleted when done with. bool FileInterner::idocToFile(TempFile& otemp, const string& tofile, @@ -904,10 +888,7 @@ bool FileInterner::idocToFile(TempFile& otemp, const string& tofile, LOGDEB(("FileInterner::idocToFile\n")); idoc.dump(); - string tmpdir, reason; - if (!maketmpdir(tmpdir, reason)) - return false; - DirWiper wiper(tmpdir); + TempDir tmpdir; // We set FIF_forPreview for consistency with the previous version // which determined this by looking at mtype!=null. Probably diff --git a/src/internfile/internfile.h b/src/internfile/internfile.h index 79340fac..72057703 100644 --- a/src/internfile/internfile.h +++ b/src/internfile/internfile.h @@ -89,7 +89,7 @@ class FileInterner { * mime type for the uncompressed version. */ FileInterner(const string &fn, const struct stat *stp, - RclConfig *cnf, const string& td, int flags, + RclConfig *cnf, TempDir &td, int flags, const string *mtype = 0); /** @@ -97,7 +97,7 @@ class FileInterner { * This is mainly for data extracted from the web cache. The mime type * must be set, input must be uncompressed. */ - FileInterner(const string &data, RclConfig *cnf, const string& td, + FileInterner(const string &data, RclConfig *cnf, TempDir &td, int flags, const string& mtype); /** @@ -106,7 +106,7 @@ class FileInterner { * best. This is only used at query time, the idoc was built from index * data. */ - FileInterner(const Rcl::Doc& idoc, RclConfig *cnf, const string& td, + FileInterner(const Rcl::Doc& idoc, RclConfig *cnf, TempDir &td, int flags); ~FileInterner(); @@ -171,7 +171,7 @@ class FileInterner { string m_targetMType; string m_reachedMType; // target or text/plain // m_tdir and m_tfile are used only for decompressing input file if needed - const string& m_tdir; + TempDir &m_tdir; string m_tfile; bool m_ok; // Set after construction if ok #ifdef RCL_USE_XATTR @@ -196,10 +196,9 @@ class FileInterner { // Pseudo-constructors void init(const string &fn, const struct stat *stp, - RclConfig *cnf, const string& td, int flags, - const string *mtype = 0); - void init(const string &data, RclConfig *cnf, const string& td, - int flags, const string& mtype); + RclConfig *cnf, int flags, const string *mtype = 0); + void init(const string &data, RclConfig *cnf, int flags, + const string& mtype); void initcommon(RclConfig *cnf, int flags); void tmpcleanup(); diff --git a/src/kde/kioslave/recoll/htmlif.cpp b/src/kde/kioslave/recoll/htmlif.cpp index 825a7517..7db377c0 100644 --- a/src/kde/kioslave/recoll/htmlif.cpp +++ b/src/kde/kioslave/recoll/htmlif.cpp @@ -233,25 +233,20 @@ public: void RecollProtocol::showPreview(const Rcl::Doc& idoc) { - string tmpdir; - string reason; - if (!maketmpdir(tmpdir, reason)) { - error(KIO::ERR_SLAVE_DEFINED, "Cannot create temporary directory"); + TempDir tmpdir; + if (!tmpdir.ok()) { + error(KIO::ERR_SLAVE_DEFINED, "Cannot create temp directory"); return; } + FileInterner interner(idoc, o_rclconfig, tmpdir, FileInterner::FIF_forPreview); Rcl::Doc fdoc; string ipath = idoc.ipath; if (!interner.internfile(fdoc, ipath)) { - wipedir(tmpdir); - rmdir(tmpdir.c_str()); error(KIO::ERR_SLAVE_DEFINED, "Cannot convert file to internal format"); return; } - wipedir(tmpdir); - rmdir(tmpdir.c_str()); - if (!interner.get_html().empty()) { fdoc.text = interner.get_html(); fdoc.mimetype = "text/html"; diff --git a/src/qtgui/preview_w.cpp b/src/qtgui/preview_w.cpp index 8da35d71..cfc95708 100644 --- a/src/qtgui/preview_w.cpp +++ b/src/qtgui/preview_w.cpp @@ -763,7 +763,7 @@ class LoadThread : public QThread { Rcl::Doc& out; const Rcl::Doc& idoc; string filename; - string tmpdir; + TempDir tmpdir; int loglevel; public: string missing; @@ -773,18 +773,14 @@ class LoadThread : public QThread { loglevel = DebugLog::getdbl()->getlevel(); } ~LoadThread() { - if (tmpdir.length()) { - wipedir(tmpdir); - rmdir(tmpdir.c_str()); - } } virtual void run() { DebugLog::getdbl()->setloglevel(loglevel); string reason; - if (!maketmpdir(tmpdir, reason)) { + if (!tmpdir.ok()) { QMessageBox::critical(0, "Recoll", Preview::tr("Cannot create temporary directory")); - LOGERR(("Preview: %s\n", reason.c_str())); + LOGERR(("Preview: %s\n", tmpdir.getreason().c_str())); *statusp = -1; return; } diff --git a/src/query/recollq.cpp b/src/query/recollq.cpp index ef4b96d3..a9643db3 100644 --- a/src/query/recollq.cpp +++ b/src/query/recollq.cpp @@ -46,18 +46,9 @@ using namespace std; #include "transcode.h" #include "textsplit.h" -bool dump_contents(RclConfig *rclconfig, string& tmpdir, Rcl::Doc& idoc) +bool dump_contents(RclConfig *rclconfig, TempDir& tmpdir, Rcl::Doc& idoc) { - if (tmpdir.empty() || access(tmpdir.c_str(), 0) < 0) { - string reason; - if (!maketmpdir(tmpdir, reason)) { - cerr << "Cannot create temporary directory: " - << reason << endl; - return false; - } - } - wipedir(tmpdir); - FileInterner interner(idoc, rclconfig, tmpdir, + FileInterner interner(idoc, rclconfig, tmpdir, FileInterner::FIF_forPreview); Rcl::Doc fdoc; string ipath = idoc.ipath; @@ -263,7 +254,12 @@ int recollq(RclConfig **cfp, int argc, char **argv) cout << cnt << " results (printing " << limit << " max):" << endl; } - string tmpdir; + TempDir tmpdir; + if (!tmpdir.ok()) { + cerr << "Can't create temporary directory: " << + tmpdir.getreason() << endl; + exit(1); + } for (int i = 0; i < limit; i++) { Rcl::Doc doc; if (!query.getDoc(i, doc)) @@ -300,14 +296,6 @@ int recollq(RclConfig **cfp, int argc, char **argv) } } - // Maybe clean up temporary directory - if (tmpdir.length()) { - wipedir(tmpdir); - if (rmdir(tmpdir.c_str()) < 0) { - cerr << "Cannot clear temp dir " << tmpdir << endl; - } - } - return 0; } diff --git a/src/utils/Makefile b/src/utils/Makefile index dc31e8fd..6c139a49 100644 --- a/src/utils/Makefile +++ b/src/utils/Makefile @@ -90,7 +90,7 @@ trsmallut.o : smallut.cpp smallut.h WIPEDIR_OBJS= trwipedir.o $(BIGLIB) wipedir : $(WIPEDIR_OBJS) - $(CXX) $(ALL_CXXFLAGS) -o wipedir $(WIPEDIR_OBJS) $(LIBICONV) + $(CXX) $(ALL_CXXFLAGS) -o wipedir $(WIPEDIR_OBJS) -L/opt/local/lib -liconv trwipedir.o : wipedir.cpp $(CXX) $(ALL_CXXFLAGS) -DTEST_WIPEDIR -c -o trwipedir.o \ wipedir.cpp diff --git a/src/utils/pathut.cpp b/src/utils/pathut.cpp index 0f5afdfb..08480ce6 100644 --- a/src/utils/pathut.cpp +++ b/src/utils/pathut.cpp @@ -26,22 +26,6 @@ static char rcsid[] = "@(#$Id: pathut.cpp,v 1.23 2008-11-24 15:47:40 dockes Exp #include #include #include -#include -#include - -#include -#include -#include -#ifndef NO_NAMESPACES -using std::string; -using std::list; -using std::stack; -#endif /* NO_NAMESPACES */ - -#include "autoconfig.h" -#include "pathut.h" -#include "transcode.h" - #include // Let's include all files where statfs can be defined and hope for no // conflict... @@ -58,6 +42,21 @@ using std::stack; #include #endif +#include +#include +#include +#include +#include +#ifndef NO_NAMESPACES +using std::string; +using std::list; +using std::stack; +#endif /* NO_NAMESPACES */ + +#include "pathut.h" +#include "transcode.h" +#include "wipedir.h" + bool fsocc(const string &path, int *pc, long *blocks) { #ifdef sun @@ -155,7 +154,7 @@ TempFileInternal::TempFileInternal(const string& suffix) int fd; if ((fd = mkstemp(cp)) < 0) { free(cp); - m_reason = "maketmpdir: mkstemp failed\n"; + m_reason = "TempFileInternal: mkstemp failed\n"; return; } close(fd); @@ -177,6 +176,34 @@ TempFileInternal::~TempFileInternal() unlink(m_filename.c_str()); } +TempDir::TempDir() +{ + if (!maketmpdir(m_dirname, m_reason)) { + m_dirname.erase(); + return; + } +} + +TempDir::~TempDir() +{ + if (!m_dirname.empty()) { + (void)wipedir(m_dirname, true, true); + m_dirname.erase(); + } +} + +bool TempDir::wipe() +{ + if (m_dirname.empty()) { + m_reason = "TempDir::wipe: no directory !\n"; + return false; + } + if (wipedir(m_dirname, false, true)) { + m_reason = "TempDir::wipe: wipedir failed\n"; + return false; + } + return true; +} void path_catslash(string &s) { if (s.empty() || s[s.length() - 1] != '/') diff --git a/src/utils/pathut.h b/src/utils/pathut.h index abd5e7a7..c03eb12f 100644 --- a/src/utils/pathut.h +++ b/src/utils/pathut.h @@ -87,4 +87,18 @@ private: typedef RefCntr TempFile; +/// Temporary directory class +class TempDir { +public: + TempDir(); + ~TempDir(); + const char *dirname() {return m_dirname.c_str();} + const string &getreason() {return m_reason;} + bool ok() {return !m_dirname.empty();} + bool wipe(); +private: + string m_dirname; + string m_reason; +}; + #endif /* _PATHUT_H_INCLUDED_ */ diff --git a/src/utils/transcode.cpp b/src/utils/transcode.cpp index 50c357c8..b34ded15 100644 --- a/src/utils/transcode.cpp +++ b/src/utils/transcode.cpp @@ -19,8 +19,7 @@ static char rcsid[] = "@(#$Id: transcode.cpp,v 1.12 2008-09-15 08:01:29 dockes E */ #ifndef TEST_TRANSCODE - -#include +#include "autoconfig.h" #include #include @@ -28,11 +27,11 @@ static char rcsid[] = "@(#$Id: transcode.cpp,v 1.12 2008-09-15 08:01:29 dockes E using std::string; #endif /* NO_NAMESPACES */ +#include #include #include "transcode.h" #include "debuglog.h" -#include "autoconfig.h" #ifdef RCL_ICONV_INBUF_CONST #define ICV_P2_TYPE const char** diff --git a/src/utils/wipedir.cpp b/src/utils/wipedir.cpp index 22c8593f..4519f18a 100644 --- a/src/utils/wipedir.cpp +++ b/src/utils/wipedir.cpp @@ -37,7 +37,7 @@ using namespace std; #include "pathut.h" #include "wipedir.h" -int wipedir(const string& dir) +int wipedir(const string& dir, bool selfalso, bool recurse) { struct stat st; int statret; @@ -78,7 +78,15 @@ int wipedir(const string& dir) goto out; } if (S_ISDIR(st.st_mode)) { - remaining++; + if (recurse) { + int rr = wipedir(fn, true, true); + if (rr == -1) + goto out; + else + remaining += rr; + } else { + remaining++; + } } else { if (unlink(fn.c_str()) < 0) { LOGERR(("wipedir: cant unlink %s, errno %d\n", @@ -89,6 +97,14 @@ int wipedir(const string& dir) } ret = remaining; + if (selfalso && ret == 0) { + if (rmdir(dir.c_str()) < 0) { + LOGERR(("wipedir: rmdir(%s) failed, errno %d\n", + dir.c_str(), errno)); + ret = -1; + } + } + out: if (d) closedir(d); @@ -103,15 +119,50 @@ int wipedir(const string& dir) #include "wipedir.h" 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 [] = +"wipedir [-r -s] topdir\n" +" -r : recurse\n" +" -s : also delete topdir\n" +; +static void +Usage(void) +{ + fprintf(stderr, "%s: usage:\n%s", thisprog, usage); + exit(1); +} int main(int argc, const char **argv) { - if (argc != 2) { - fprintf(stderr, "Usage: wipedir \n"); - exit(1); + thisprog = argv[0]; + argc--; argv++; + + while (argc > 0 && **argv == '-') { + (*argv)++; + if (!(**argv)) + /* Cas du "adb - core" */ + Usage(); + while (**argv) + switch (*(*argv)++) { + case 'r': op_flags |= OPT_r; break; + case 's': op_flags |= OPT_s; break; + default: Usage(); break; + } + b1: argc--; argv++; } - string dir = argv[1]; - int cnt = wipedir(dir); + + if (argc != 1) + Usage(); + + string dir = *argv++;argc--; + + bool topalso = ((op_flags&OPT_s) != 0); + bool recurse = ((op_flags&OPT_r) != 0); + int cnt = wipedir(dir, topalso, recurse); printf("wipedir returned %d\n", cnt); exit(0); } diff --git a/src/utils/wipedir.h b/src/utils/wipedir.h index 709f60df..6580f3b3 100644 --- a/src/utils/wipedir.h +++ b/src/utils/wipedir.h @@ -21,9 +21,9 @@ #include /** - * Remove all files inside directory (not recursive). + * Remove all files inside directory. * @return 0 if ok, count of remaining entries (ie: subdirs), or -1 for error */ -int wipedir(const std::string& dirname); +int wipedir(const std::string& dirname, bool topalso = 0, bool recurse = 0); #endif /* _FILEUT_H_INCLUDED_ */