7

I have a path to the directory, and I want to traverse through all of its sub-directories, collecting files' pathes by the way.

namespace fs = boost::filesystem;

std::vector<fs::path> traverse_if_directory(fs::path& f) {
    std::vector<fs::path> result;
    if (fs::is_directory(f)) {      
        for (fs::recursive_directory_iterator it(f), eit; it != eit; ++it) {
            if (!fs::is_directory(it->path())) {
                result.push_back(it->path());
            }       
        }
    }
    else {
        result.push_back(f);
    }

    return result;
}

Unfortunately, in the middle of the traversing, I stumble upon a directory which I have no rights to look at, and the code above throws. But obviously, in this scenario it's not an exception, I should just go on, skipping this locked directory.

But how do I do this?

Joker_vD
  • 3,715
  • 1
  • 28
  • 42
  • 2
    what about... catching the exception? – PlasmaHH Apr 12 '13 at 11:23
  • And what do I do when I catch it? At this point the iterator will be already destroyed, I can't continue iteration from within the `catch` clause! – Joker_vD Apr 12 '13 at 11:25
  • 1
    check the permissions of the directory before traversing it, ignore it if you dont have permission [link](http://stackoverflow.com/questions/9776050/how-to-get-file-permissions-with-c-boost-library) – ldgorman Apr 12 '13 at 11:39

2 Answers2

10

Ha, figured it out, there is a way:

std::vector<fs::path> traverse_if_directory(fs::path& f) {
    std::vector<fs::path> result;
    boost::system::error_code ec;

    if (fs::is_directory(f)) {      
        for (
             fs::recursive_directory_iterator it(f, ec), eit;
             it != eit;
             it.increment(ec)
            ) {
            if (ec) {
                it.pop();
                continue;
            }
            if (!fs::is_directory(it->path())) {
                result.push_back(it->path());
            }
        }
    }
    else {
        result.push_back(f);
    }

    return result;
}

There is a non-throwing overload which accepts an output parameter of type boost::system::error_code, so I can just check after each increment if there were any error.

Joker_vD
  • 3,715
  • 1
  • 28
  • 42
  • 2
    Apparently there's a bug in `boost::filesystem::recursive_directory_iterator` (when using V3 of boost filesystem), so that it will indeed throw, even if the no-throw version is used: https://svn.boost.org/trac/boost/ticket/4494 – Andreas Magnusson Apr 17 '14 at 14:11
  • Great just what I needed. I stumbled upon the same problem years ago. http://boost.2283326.n4.nabble.com/Filesystem-directory-iteration-throws-td2598550.html – gast128 Mar 29 '18 at 13:44
1

Joker_vD's answer crashes when an error occurs for the last directory entry. For example, when the target directory contains a single subdirectory without permissions. The cause is the 'it.pop()' which apparently isn't needed. Also, the 'continue' shouldn't be done, as it would skip the next entry. After an error the iterator already points to the next valid entry, or is equal to the end iterator.

This is a corrected version:

std::vector<fs::path> traverse_if_directory(const fs::path& f) {
    std::vector<fs::path> result;
    boost::system::error_code ec;

    if (fs::is_directory(f)) {
        for (fs::recursive_directory_iterator it{f, ec}, end; it != end; it.increment(ec)) {
            if (!fs::is_directory(it->path())) {
                result.push_back(it->path());
            }
        }
    }
    else {
        result.push_back(f);
    }

    return result;
}
Max
  • 81
  • 7