aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-07-09 12:25:47 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-07-09 12:25:47 +0200
commit02af7e788f3fef0dbba0ba49442054fb451f5bdc (patch)
tree5e0295bc0d06579cfffd0df3ad176945c9c51c79
parent38c8f0efd8f033be8fb8278aa5d0eb704dedee55 (diff)
Implement directory iteration support
-rw-r--r--butl/filesystem119
-rw-r--r--butl/filesystem.cxx133
-rw-r--r--butl/filesystem.ixx65
-rw-r--r--tests/buildfile2
-rw-r--r--tests/dir-iterator/buildfile7
-rw-r--r--tests/dir-iterator/driver.cxx55
6 files changed, 376 insertions, 5 deletions
diff --git a/butl/filesystem b/butl/filesystem
index 18ba48e..4d0b7ef 100644
--- a/butl/filesystem
+++ b/butl/filesystem
@@ -7,6 +7,14 @@
#include <sys/types.h> // mode_t
+#ifndef _WIN32
+# include <dirent.h> // DIR, struct dirent, *dir()
+#endif
+
+#include <cstddef> // ptrdiff_t
+#include <utility> // move()
+#include <iterator>
+
#include <butl/path>
#include <butl/timestamp>
@@ -65,6 +73,117 @@ namespace butl
rmfile_status
try_rmfile (const path&);
+
+ // Directory entry iteration.
+ //
+ enum class entry_type
+ {
+ unknown,
+ regular,
+ directory,
+ symlink,
+ other
+ };
+
+ class dir_entry
+ {
+ public:
+ typedef butl::path path_type;
+
+ entry_type
+ type () const;
+
+ // Symlink target type in case of the symlink, type() otherwise.
+ //
+ entry_type
+ ltype () const;
+
+ // Entry path (excluding the base). To get the full path, do
+ // base () / path ().
+ //
+ const path_type&
+ path () const {return p_;}
+
+ const dir_path&
+ base () const {return b_;}
+
+ dir_entry () = default;
+ dir_entry (entry_type t, path_type p, dir_path b)
+ : t_ (t), p_ (std::move (p)), b_ (std::move (b)) {}
+
+ private:
+ entry_type
+ type (bool link) const;
+
+ private:
+ friend class dir_iterator;
+
+ mutable entry_type t_ = entry_type::unknown; // Lazy evaluation.
+ mutable entry_type lt_ = entry_type::unknown; // Lazy evaluation.
+ path_type p_;
+ dir_path b_;
+ };
+
+ class dir_iterator
+ {
+ public:
+ typedef dir_entry value_type;
+ typedef const dir_entry* pointer;
+ typedef const dir_entry& reference;
+ typedef std::ptrdiff_t difference_type;
+ typedef std::input_iterator_tag iterator_category;
+
+ ~dir_iterator ();
+ dir_iterator () = default;
+
+ explicit
+ dir_iterator (const dir_path&);
+
+ dir_iterator (const dir_iterator&) = delete;
+ dir_iterator& operator= (const dir_iterator&) = delete;
+
+ dir_iterator (dir_iterator&& x);
+ dir_iterator& operator= (dir_iterator&&);
+
+ dir_iterator& operator++ () {next (); return *this;}
+
+ reference operator* () const {return e_;}
+ pointer operator-> () const {return &e_;}
+
+ friend bool operator== (const dir_iterator&, const dir_iterator&);
+ friend bool operator!= (const dir_iterator&, const dir_iterator&);
+
+ private:
+ void
+ next ();
+
+ private:
+ dir_entry e_;
+
+#ifndef _WIN32
+ DIR* h_ {nullptr};
+#else
+ // Check move implementation.
+ //
+# error Win32 implementation lacking
+#endif
+ };
+
+ // Range-based for loop support.
+ //
+ // for (const auto& de: dir_iterator (dir_path ("/tmp"))) ...
+ //
+ // Note that the "range" (which is the "begin" iterator), is no
+ // longer usable. In other words, don't do this:
+ //
+ // dir_iterator i (...);
+ // for (...: i) ...
+ // ++i; // Invalid.
+ //
+ inline dir_iterator begin (dir_iterator& i) {return std::move (i);}
+ inline dir_iterator end (const dir_iterator&) {return dir_iterator ();}
}
+#include <butl/filesystem.ixx>
+
#endif // BUTL_FILESYSTEM
diff --git a/butl/filesystem.cxx b/butl/filesystem.cxx
index 7c8893e..4cf60b2 100644
--- a/butl/filesystem.cxx
+++ b/butl/filesystem.cxx
@@ -8,6 +8,7 @@
#include <sys/types.h> // stat
#include <sys/stat.h> // stat, lstat(), S_IS*, mkdir()
+#include <memory> // unique_ptr
#include <system_error>
using namespace std;
@@ -17,25 +18,25 @@ namespace butl
// Figuring out whether we have the nanoseconds in some form.
//
template <typename S>
- constexpr auto nsec (const S* s) -> decltype(s->st_mtim.tv_nsec)
+ inline constexpr auto nsec (const S* s) -> decltype(s->st_mtim.tv_nsec)
{
return s->st_mtim.tv_nsec; // POSIX (GNU/Linux, Solaris).
}
template <typename S>
- constexpr auto nsec (const S* s) -> decltype(s->st_mtimespec.tv_nsec)
+ inline constexpr auto nsec (const S* s) -> decltype(s->st_mtimespec.tv_nsec)
{
return s->st_mtimespec.tv_nsec; // MacOS X.
}
template <typename S>
- constexpr auto nsec (const S* s) -> decltype(s->st_mtime_n)
+ inline constexpr auto nsec (const S* s) -> decltype(s->st_mtime_n)
{
return s->st_mtime_n; // AIX 5.2 and later.
}
template <typename S>
- constexpr int nsec (...) {return 0;}
+ inline constexpr int nsec (...) {return 0;}
timestamp
file_mtime (const path& p)
@@ -140,4 +141,128 @@ namespace butl
return r;
}
+
+#ifndef _WIN32
+
+ // dir_entry
+ //
+ entry_type dir_entry::
+ type (bool link) const
+ {
+ path_type p (b_ / p_);
+ struct stat s;
+ if ((link
+ ? ::stat (p.string ().c_str (), &s)
+ : ::lstat (p.string ().c_str (), &s)) != 0)
+ {
+ throw system_error (errno, system_category ());
+ }
+
+ entry_type r;
+
+ if (S_ISREG (s.st_mode))
+ r = entry_type::regular;
+ else if (S_ISDIR (s.st_mode))
+ r = entry_type::directory;
+ else if (S_ISLNK (s.st_mode))
+ r = entry_type::symlink;
+ else
+ r = entry_type::other;
+
+ return r;
+ }
+
+ // dir_iterator
+ //
+ struct dir_deleter
+ {
+ void operator() (DIR* p) const {if (p != nullptr) ::closedir (p);}
+ };
+
+ dir_iterator::
+ dir_iterator (const dir_path& d)
+ {
+ unique_ptr<DIR, dir_deleter> h (::opendir (d.string ().c_str ()));
+ h_ = h.get ();
+
+ if (h_ == nullptr)
+ throw system_error (errno, system_category ());
+
+ next ();
+
+ if (h_ != nullptr)
+ e_.b_ = d;
+
+ h.release ();
+ }
+
+ template <typename D>
+ inline /*constexpr*/ entry_type d_type (const D* d, decltype(d->d_type)*)
+ {
+ switch (d->d_type)
+ {
+#ifdef DT_DIR
+ case DT_DIR: return entry_type::directory;
+#endif
+#ifdef DT_REG
+ case DT_REG: return entry_type::regular;
+#endif
+#ifdef DT_LNK
+ case DT_LNK: return entry_type::symlink;
+#endif
+#ifdef DT_BLK
+ case DT_BLK:
+#endif
+#ifdef DT_CHR
+ case DT_CHR:
+#endif
+#ifdef DT_FIFO
+ case DT_FIFO:
+#endif
+#ifdef DT_SOCK
+ case DT_SOCK:
+#endif
+ return entry_type::other;
+
+ default: return entry_type::unknown;
+ }
+ }
+
+ template <typename D>
+ inline constexpr entry_type d_type (...) {return entry_type::unknown;}
+
+ void dir_iterator::
+ next ()
+ {
+ for (;;)
+ {
+ errno = 0;
+ if (struct dirent* de = ::readdir (h_))
+ {
+ const char* n (de->d_name);
+
+ // Skip '.' and '..'.
+ //
+ if (n[0] == '.' && (n[1] == '\0' || (n[1] == '.' && n[2] == '\0')))
+ continue;
+
+ e_.p_ = path (n);
+ e_.t_ = d_type<struct dirent> (de, nullptr);
+ e_.lt_ = entry_type::unknown;
+ }
+ else if (errno == 0)
+ {
+ // End of stream.
+ //
+ ::closedir (h_);
+ h_ = nullptr;
+ }
+ else
+ throw system_error (errno, system_category ());
+
+ break;
+ }
+ }
+#else
+#endif
}
diff --git a/butl/filesystem.ixx b/butl/filesystem.ixx
new file mode 100644
index 0000000..7aa3622
--- /dev/null
+++ b/butl/filesystem.ixx
@@ -0,0 +1,65 @@
+// file : butl/filesystem.ixx -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+namespace butl
+{
+ // dir_entry
+ //
+ inline entry_type dir_entry::
+ type () const
+ {
+ return t_ != entry_type::unknown ? t_ : (t_ = type (false));
+ }
+
+ inline entry_type dir_entry::
+ ltype () const
+ {
+ entry_type t (type ());
+ return t != entry_type::symlink
+ ? t
+ : lt_ != entry_type::unknown ? lt_ : (lt_ = type (true));
+ }
+
+ // dir_iterator
+ //
+ inline dir_iterator::
+ dir_iterator (dir_iterator&& x): e_ (std::move (x.e_)), h_ (x.h_)
+ {
+ x.h_ = nullptr;
+ }
+
+ inline dir_iterator& dir_iterator::
+ operator= (dir_iterator&& x)
+ {
+ if (this != &x)
+ {
+ e_ = std::move (x.e_);
+ h_ = x.h_;
+ x.h_ = nullptr;
+ }
+ return *this;
+ }
+
+ inline bool
+ operator== (const dir_iterator& x, const dir_iterator& y)
+ {
+ return x.h_ == y.h_;
+ }
+
+ inline bool
+ operator!= (const dir_iterator& x, const dir_iterator& y)
+ {
+ return !(x == y);
+ }
+
+#ifndef _WIN32
+ inline dir_iterator::
+ ~dir_iterator ()
+ {
+ if (h_ != nullptr)
+ ::closedir (h_); // Ignore any errors.
+ }
+#else
+#endif
+}
diff --git a/tests/buildfile b/tests/buildfile
index ae4bbb3..684221b 100644
--- a/tests/buildfile
+++ b/tests/buildfile
@@ -2,6 +2,6 @@
# copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
# license : MIT; see accompanying LICENSE file
-d = path/ prefix-map/
+d = dir-iterator/ path/ prefix-map/
.: $d
include $d
diff --git a/tests/dir-iterator/buildfile b/tests/dir-iterator/buildfile
new file mode 100644
index 0000000..fabea4d
--- /dev/null
+++ b/tests/dir-iterator/buildfile
@@ -0,0 +1,7 @@
+# file : tests/dir-iterator/buildfile
+# copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+exe{driver}: cxx{driver} ../../butl/lib{butl}
+
+include ../../butl/
diff --git a/tests/dir-iterator/driver.cxx b/tests/dir-iterator/driver.cxx
new file mode 100644
index 0000000..09a259f
--- /dev/null
+++ b/tests/dir-iterator/driver.cxx
@@ -0,0 +1,55 @@
+// file : tests/dir-iterator/driver.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <cstddef> // size_t
+#include <cassert>
+#include <iostream>
+
+#include <butl/path>
+#include <butl/path-io>
+#include <butl/filesystem>
+
+using namespace std;
+using namespace butl;
+
+static const char* entry_type_string[] =
+{
+ "unk", "reg", "dir", "sym", "oth"
+};
+
+inline ostream&
+operator<< (ostream& os, entry_type e)
+{
+ return os << entry_type_string[static_cast<size_t> (e)];
+}
+
+int
+main (int argc, const char* argv[])
+{
+ if (argc != 2)
+ {
+ cerr << "usage: " << argv[0] << " <dir>" << endl;
+ return 1;
+ }
+
+ try
+ {
+ for (const dir_entry& de: dir_iterator (dir_path (argv[1])))
+ {
+ cerr << de.type () << " ";
+
+ if (de.type () == entry_type::symlink)
+ cerr << de.ltype ();
+ else
+ cerr << " ";
+
+ cerr << " " << de.path () << endl;
+ }
+ }
+ catch (const exception& e)
+ {
+ cerr << argv[1] << ": " << e.what () << endl;
+ return 1;
+ }
+}