From fcadd7bad55f23ed86e3056bb34f2d5c6f4f3af5 Mon Sep 17 00:00:00 2001 From: Gerd von Egidy Date: Thu, 17 Dec 2015 00:32:07 +0100 Subject: [PATCH] implement du() --- src/filefunc.cpp | 62 ++++++++++++++++++++++++++++++++++++++++++++++++ src/filefunc.hxx | 2 +- test/test_filefunc.cpp | 29 ++++++++++++++++++++++ 3 files changed, 92 insertions(+), 1 deletions(-) diff --git a/src/filefunc.cpp b/src/filefunc.cpp index 575be4d..80790fc 100644 --- a/src/filefunc.cpp +++ b/src/filefunc.cpp @@ -986,5 +986,67 @@ long long get_free_diskspace(const std::string& path) return free_bytes; } +/** + * like du(1): return the number bytes used by a directory structure + * @param path File or directory to start counting recursively + * @param error Will contain the error if the return value is -1 [optional] + * @return size in bytes on success, -1 on error + * + * @attention This function does currently not consider that hardlinked files need + * space on disk only once. But this may be added later if needed. So make + * sure that calling functions don't depend on either behavior! + */ +long long du(const std::string &path, std::string *error) +{ + long long sum = 0; + + try + { + Stat sp(path, false); // don't dereference symlinks here + if (!sp) + throw runtime_error("can't stat " + path); + + // always add the space used, even if we have a directory, symlink or whatever: + // they need space on disk too + + sum+=sp.bytes_on_disk(); + + if (sp.is_directory()) + { + std::vector dirents = get_dir(path, false); + BOOST_FOREACH(const std::string &filename, dirents) + { + // Delete subdir or file. + long long rtn = du(path + "/" + filename, error); + if (rtn == -1) + return -1; + sum+=rtn; + } + } + } + catch (exception &e) + { + if (error) + { + ostringstream out; + out << e.what() << " (" << strerror(errno) << ")"; + *error = out.str(); + } + return -1; + } + catch (...) + { + if (error) + { + ostringstream out; + out << "unknown error (" << strerror(errno) << ")"; + *error = out.str(); + } + return -1; + } + + return sum; +} // eo du + } // eo namespace I2n diff --git a/src/filefunc.hxx b/src/filefunc.hxx index e338172..5fe6ccc 100644 --- a/src/filefunc.hxx +++ b/src/filefunc.hxx @@ -201,7 +201,7 @@ bool chdir(const std::string &path, std::string *error=NULL); mode_t umask(mode_t mask); long long get_free_diskspace(const std::string& path); - +long long du(const std::string &path, std::string *error=NULL); /* ** more specialized tool function(s) diff --git a/test/test_filefunc.cpp b/test/test_filefunc.cpp index 0a8426a..237e8b2 100644 --- a/test/test_filefunc.cpp +++ b/test/test_filefunc.cpp @@ -608,4 +608,33 @@ BOOST_AUTO_TEST_CASE(FreeDiskSpace2) BOOST_CHECK_EQUAL( -1, get_free_diskspace("/this/path/is/really/bogus") ); } +BOOST_AUTO_TEST_CASE(TestDu) +{ + // Create unique directory + string unique_dir = I2n::mkdtemp("foobar.XXXXXX"); + BOOST_REQUIRE(unique_dir.size() > 0); + + // Test if it's really a directory + BOOST_CHECK_EQUAL(true, Stat(unique_dir).is_directory()); + + // Create dirs and files below it + const std::string sub_dir = unique_dir + "/" + "some_subdir"; + const std::string some_file = sub_dir + "/" + "some_file"; + BOOST_CHECK_EQUAL(true, I2n::mkdir(sub_dir)); + BOOST_CHECK_EQUAL(true, write_file(some_file, "foobar and some more data, even more that Tom would like to see in one line without linebreak but I still want to have it all here")); + + inpipestream cmd_du(string("du --block-size=1 --sum ")+unique_dir+" | cut -f 1"); + string dustr; + if (cmd_du) + getline(cmd_du, dustr); + + BOOST_CHECK_EQUAL( string_to(dustr), du(unique_dir) ); + + // Unlink it + BOOST_CHECK_EQUAL(true, I2n::recursive_delete(unique_dir)); + BOOST_CHECK_EQUAL(false, Stat(unique_dir).is_directory()); +} + + + BOOST_AUTO_TEST_SUITE_END() -- 1.7.1