about summary refs log tree commit diff
path: root/src/references.cc
diff options
context:
space:
mode:
authorEelco Dolstra <e.dolstra@tudelft.nl>2003-07-14T10·23+0000
committerEelco Dolstra <e.dolstra@tudelft.nl>2003-07-14T10·23+0000
commit3509299aca833ed50faab146f985853255041cb2 (patch)
tree4c5edc08eaa798790f72715ccbc8f19b0ef8ac47 /src/references.cc
parent135b7d54db4e0ca56bda67946432fcf9d4f3ac5c (diff)
* After building, scan for actual file system references as
  opposed to declared references.  This prunes the reference
  graph, thus allowing better garbage collection and more
  efficient derivate distribution.

Diffstat (limited to 'src/references.cc')
-rw-r--r--src/references.cc98
1 files changed, 98 insertions, 0 deletions
diff --git a/src/references.cc b/src/references.cc
new file mode 100644
index 0000000000..de7a4b339c
--- /dev/null
+++ b/src/references.cc
@@ -0,0 +1,98 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <fcntl.h>
+
+#include "references.hh"
+#include "hash.hh"
+
+
+static void search(const string & s,
+    Strings & refs, Strings & seen)
+{
+    for (Strings::iterator i = refs.begin();
+         i != refs.end(); )
+    {
+        if (s.find(*i) == string::npos)
+            i++;
+        else {
+            debug(format("found reference to `%1%'") % *i);
+            seen.push_back(*i);
+            i = refs.erase(i);
+        }
+    }
+}
+
+
+void checkPath(const string & path,
+    Strings & refs, Strings & seen)
+{
+    struct stat st;
+    if (lstat(path.c_str(), &st))
+        throw SysError(format("getting attributes of path `%1%'") % path);
+
+    if (S_ISDIR(st.st_mode)) {
+        DIR * dir = opendir(path.c_str());
+
+        struct dirent * dirent;
+        while (errno = 0, dirent = readdir(dir)) {
+            string name = dirent->d_name;
+            if (name == "." || name == "..") continue;
+            search(name, refs, seen);
+            checkPath(path + "/" + name, refs, seen);
+        }
+
+        closedir(dir); /* !!! close on exception */
+    }
+
+    else if (S_ISREG(st.st_mode)) {
+        
+        debug(format("checking `%1%'") % path);
+
+        int fd = open(path.c_str(), O_RDONLY);
+        if (fd == -1) throw SysError(format("opening file `%1%'") % path);
+
+        char * buf = new char[st.st_size];
+
+        if (read(fd, buf, st.st_size) != st.st_size)
+            throw SysError(format("reading file %1%") % path);
+
+        search(string(buf, st.st_size), refs, seen);
+        
+        delete buf; /* !!! autodelete */
+
+        close(fd); /* !!! close on exception */
+    }
+    
+    else if (S_ISLNK(st.st_mode)) {
+        char buf[st.st_size];
+        if (readlink(path.c_str(), buf, st.st_size) != st.st_size)
+            throw SysError(format("reading symbolic link `%1%'") % path);
+        search(string(buf, st.st_size), refs, seen);
+    }
+    
+    else throw Error(format("unknown file type: %1%") % path);
+}
+
+
+Strings filterReferences(const string & path, const Strings & _refs)
+{
+    Strings refs;
+    Strings seen;
+
+    /* For efficiency (and a higher hit rate), just search for the
+       hash part of the file name.  (This assumes that all references
+       have the form `HASH-bla'). */
+    for (Strings::const_iterator i = _refs.begin();
+         i != _refs.end(); i++)
+    {
+        string s = string(baseNameOf(*i), 0, 32);
+        parseHash(s);
+        refs.push_back(s);
+    }
+
+    checkPath(path, refs, seen);
+
+    return seen;
+}