implement du()
authorGerd von Egidy <gerd.von.egidy@intra2net.com>
Wed, 16 Dec 2015 23:32:07 +0000 (00:32 +0100)
committerGerd von Egidy <gerd.von.egidy@intra2net.com>
Wed, 16 Dec 2015 23:32:07 +0000 (00:32 +0100)
src/filefunc.cpp
src/filefunc.hxx
test/test_filefunc.cpp

index 575be4d..80790fc 100644 (file)
@@ -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<std::string> 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
index e338172..5fe6ccc 100644 (file)
@@ -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)
index 0a8426a..237e8b2 100644 (file)
@@ -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<long long>(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()