diff --git a/src/bee-dep.c b/src/bee-dep.c
index 54d77cf..27b981f 100644
--- a/src/bee-dep.c
+++ b/src/bee-dep.c
@@ -24,466 +24,830 @@
 ** along with this program; if not, see <http://www.gnu.org/licenses/>.
 */
 
-#define _GNU_SOURCE
-
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/stat.h>
 #include <string.h>
 #include <limits.h>
 #include <dirent.h>
-#include <sys/file.h>
 #include <libgen.h>
 #include <unistd.h>
 #include <getopt.h>
-#include <errno.h>
-#include <assert.h>
+#include <fcntl.h>
 
 #include "graph.h"
 
 #define CACHENAME "index.db"
+#define TMPNAME   "index.tmp"
+#define LOCKNAME  "index.lock"
+
+#define REBUILD   1
+#define UPDATE    2
+#define REMOVE    3
+#define LIST      4
+#define CONFLICTS 5
 
-#define likely(x)       __builtin_expect(!!(x), 1)
-#define unlikely(x)     __builtin_expect(!!(x), 0)
+static char *bee_version(void)
+{
+    static char *bee_v = NULL;
 
-#define BEE_METADIR  env_bee_metadir()
-#define BEE_CACHEDIR env_bee_cachedir()
+    if (!bee_v)
+        bee_v = getenv("BEE_VERSION");
+
+    return bee_v;
+}
 
-static char *env_bee_metadir(void)
+static char *bee_metadir(void)
 {
-    static char *value = NULL;
+    static char *bee_md = NULL;
 
-    if(value)
-        return value;
+    if (!bee_md)
+        bee_md = getenv("BEE_METADIR");
 
-    value = getenv("BEE_METADIR");
+    return bee_md;
+}
+
+static char *bee_cachedir(void)
+{
+    static char *bee_cd = NULL;
 
-    if(!value)
-        value = "";
+    if (!bee_cd)
+        bee_cd = getenv("BEE_CACHEDIR");
 
-    return value;
+    return bee_cd;
 }
 
-static char *env_bee_cachedir(void)
+static void get_bee_variables(void)
 {
-    static char *value = NULL;
+    if (!bee_version()) {
+        fprintf(stderr, "BEE-ERROR: please call bee-dep from bee\n");
+        exit(1);
+    }
+
+    if (!bee_metadir()) {
+        fprintf(stderr, "BEE-ERROR: BEE_METADIR not set\n");
+        exit(1);
+    }
+
+    if (!bee_cachedir()) {
+        fprintf(stderr, "BEE_ERROR: BEE_CACHEDIR not set\n");
+        exit(1);
+    }
+}
+
+static char *cache_filename(void)
+{
+    static char cache[PATH_MAX + 1] = {0};
+
+    if (!cache[0])
+        sprintf(cache, "%s/%s", bee_cachedir(), CACHENAME);
+
+    return cache;
+}
 
-    if(value)
-        return value;
+static char *lock_filename(void)
+{
+    static char lock[PATH_MAX + 1] = {0};
 
-    value = getenv("BEE_CACHEDIR");
+    if (!lock[0])
+        sprintf(lock, "%s/%s", bee_cachedir(), LOCKNAME);
 
-    if(!value)
-        value = "";
+    return lock;
+}
 
-    return value;
+static void usage_header(void)
+{
+    printf("bee-dep v%s 2011\n"
+           "  by Matthias Ruester and Lucas Schwass\n"
+           "     Max Planck Institute for Molecular Genetics Berlin Dahlem\n\n",
+           getenv("BEE_VERSION"));
 }
 
 static void usage(void)
 {
-     printf("bee-dep v%s 2011\n"
-            "  by Matthias Ruester and Lucas Schwass\n"
-            "     Max Planck Institute for Molecular Genetics Berlin Dahlem\n\n"
-
-            "Usage: bee dep <OPTION> [PKGNAME]\n\n"
-
-            "Options:\n"
-            "    --rebuild                      rebuild the cache\n"
-            "    --update          <PKGNAME>    update the cache\n"
-            "    --print-removable <PKGNAME>    print removable files "
-                                               "from package\n"
-            "    --remove          <PKGNAME>    remove package from cache\n",
-            getenv("BEE_VERSION"));
+    usage_header();
+    printf("Usage: bee dep <command> [<args>]\n\n"
+
+           "Commands:\n"
+           "    rebuild      rebuild the cache\n"
+           "    update       update the cache for a specific package\n"
+           "    remove       remove a package from the cache\n"
+           "    list         list information\n"
+           "    conflicts    show conflicting packages\n");
 }
 
-int pkg_to_graph(struct hash *graph, char *pkgname)
+static void usage_rebuild(void)
 {
-    int ret = 1;
-    char *fname;
+    usage_header();
+    printf("Usage: bee dep rebuild\n");
+}
 
-    if(asprintf(&fname, "%s/%s/DEPENDENCIES", BEE_METADIR, pkgname) == -1) {
-        perror("bee-dep: pkg_to_graph: asprintf");
-        return -1;
-    }
+static void usage_update(void)
+{
+    usage_header();
+    printf("Usage: bee dep update [pkgname]\n");
+}
+
+static void usage_remove(void)
+{
+    usage_header();
+    printf("Usage: bee dep remove [options] <pkgname>\n\n"
+
+           "Options:\n"
+           "    --print    print which files can be deleted from the hard drive\n");
+}
+
+static void usage_list(void)
+{
+    usage_header();
+    printf("Usage: bee dep list [options]\n\n"
+
+           "Options:\n"
+           "    --packages\n"
+           "    --files        <pkg>\n"
+           "    --depending-on <pkg|file>\n"
+           "    --required-by  <pkg|file>\n"
+           "    --removable    <pkg>\n"
+           "    --provider-of  <file>\n"
+           "    --count\n");
+}
+
+static void usage_conflicts(void)
+{
+    usage_header();
+    printf("Usage: bee dep conflicts [pkgname]\n");
+}
+
+void ensure_directories(void)
+{
+    char *c;
+    int i;
+    char dir[PATH_MAX + 1] = {0};
+    struct stat st;
+
+    c = bee_cachedir();
+    i = 0;
+
+    while (*c) {
+        dir[i] = *c;
+        c++;
 
-    ret = graph_insert_nodes(graph, fname);
+        if (*c == '/' || !(*c)) {
+            if (stat(dir, &st) == -1 && mkdir(dir, 0755) == -1) {
+                perror("mkdir");
+                exit(1);
+            }
+        }
 
-    free(fname);
-    return ret;
+        i++;
+    }
 }
 
-int init_cache(struct hash *graph, char *filename)
+static struct hash *init_cache(void)
 {
     struct dirent **package;
     int i, pkg_cnt;
+    char path[PATH_MAX + 1];
+    struct stat st;
+    struct hash *graph;
 
-    /* TODO: need to handle all kinds of race conditions here 8) */
-
-    if ((pkg_cnt = scandir(BEE_METADIR, &package, 0, alphasort)) < 0) {
-        perror("bee-dep: create_cache: scandir");
-        return 0;
+    if ((pkg_cnt = scandir(bee_metadir(), &package, 0, alphasort)) < 0) {
+        perror("bee-dep: init_cache: scandir");
+        return NULL;
     }
 
+    graph = hash_new();
+
     /* skip . and .. */
     free(package[0]);
     free(package[1]);
 
     for (i = 2; i < pkg_cnt; i++) {
-        pkg_to_graph(graph, package[i]->d_name);
+        sprintf(path, "%s/%s", bee_metadir(), package[i]->d_name);
+
+        if (stat(path, &st) == -1) {
+            perror("bee-dep: init_cache: stat");
+            hash_free(graph);
+            return NULL;
+        }
+
+        if (S_ISDIR(st.st_mode)) {
+            strcat(path, "/DEPENDENCIES");
+
+            if (stat(path, &st) == -1) {
+                fprintf(stderr,
+                        "bee-dep: init_cache: missing "
+                        "DEPENDENCIES file for package \"%s\"\n",
+                        package[i]->d_name);
+                hash_free(graph);
+                return NULL;
+            }
+
+            if (graph_insert_nodes(graph, path)) {
+                hash_free(graph);
+                return NULL;
+            }
+        }
+
         free(package[i]);
     }
 
     free(package);
 
-    return save_cache(graph, filename);
+    if (save_cache(graph, cache_filename())) {
+        hash_free(graph);
+        return NULL;
+    }
+
+    return graph;
 }
 
-void unlock(FILE *cache)
+static int update_cache(struct hash *graph)
 {
-    if (flock(fileno(cache), LOCK_UN) == -1) {
-        perror("bee-dep: unlock: flock");
-        exit(EXIT_FAILURE);
-    }
+    struct dirent **package;
+    int i, pkg_cnt;
+    char path[PATH_MAX + 1];
+    struct stat st;
+    struct tree_node *t;
 
-    if (fclose(cache) == EOF) {
-        perror("bee-dep: unlock: fclose");
-        exit(EXIT_FAILURE);
+    if ((pkg_cnt = scandir(bee_metadir(), &package, 0, alphasort)) < 0) {
+        perror("bee-dep: update_cache: scandir");
+        return 1;
     }
-}
 
-void cleanup_and_exit(struct hash *h, FILE *f, int r)
-{
-    if (h)
-        hash_free(h);
+    /* skip . and .. */
+    free(package[0]);
+    free(package[1]);
 
-    if (f)
-        unlock(f);
+    /* add new (not known) packages */
+    for (i = 2; i < pkg_cnt; i++) {
+        if (hash_search(graph, package[i]->d_name))
+            continue;
 
-    exit(r);
-}
+        printf("adding %s\n", package[i]->d_name);
 
-static FILE *open_and_lock(char *filename, char *mode)
-{
-    FILE *f;
+        if (sprintf(path, "%s/%s", bee_metadir(), package[i]->d_name) < 0) {
+            perror("bee-dep: update_cache: sprintf");
+            return 1;
+        }
+
+        if (stat(path, &st) == -1) {
+            perror("bee-dep: update_cache: stat");
+            return 1;
+        }
+
+        if (S_ISDIR(st.st_mode)) {
+            strcat(path, "/DEPENDENCIES");
 
-    if ((f = fopen(filename, mode)) == NULL) {
-        perror("bee-dep: fopen");
-        exit(EXIT_FAILURE);
+            if (stat(path, &st) == -1) {
+                fprintf(stderr,
+                        "bee-dep: update_cache: missing "
+                        "DEPENDENCIES file for package \"%s\"\n",
+                        package[i]->d_name);
+                return 1;
+            }
+
+            if (graph_insert_nodes(graph, path))
+                return 1;
+        }
+
+        free(package[i]);
     }
 
-    if (flock(fileno(f), LOCK_EX) == -1) {
-        perror("bee-dep: flock");
-        exit(EXIT_FAILURE);
+    free(package);
+
+    /* remove packages which not exist anymore */
+    for (i = 0; i < TBLSIZE; i++) {
+        t = tree_first(graph->tbl[i]->root);
+
+        while (t) {
+            if (IS_PKG(t->n)) {
+                if (sprintf(path, "%s/%s", bee_metadir(), t->n->name) < 0) {
+                    perror("bee-dep: update_cache: sprintf");
+                    return 1;
+                }
+
+                if (stat(path, &st) == -1) {
+                    printf("removing %s\n", t->n->name);
+
+                    if (remove_package(graph, t->n->name))
+                        return 1;
+                }
+            }
+
+            t = tree_next(t);
+        }
     }
 
-    return f;
+    return 0;
 }
 
-/* create all directories in path with mode mode */
-int mkdirp(char *path, mode_t mode)
+struct hash *get_cache(void)
 {
-    char *dir, *pdir, *end;
-    int ret;
+    struct hash *graph;
+    struct stat st;
 
-    assert(path);
+    if (stat(cache_filename(), &st) == -1)
+        graph = init_cache();
+    else
+        graph = load_cache(cache_filename());
 
-    dir = end = path;
+    if (!graph)
+        exit(1);
 
-    while(*dir) {
-        /* skip "/" */
-        dir = end + strspn(end, "/");
+    return graph;
+}
 
-        /* skip non-"/" */
-        end = dir + strcspn(dir, "/");
+static int bee_dep_rebuild(int argc, char *argv[])
+{
+    int c, help;
+    struct hash *graph;
+    struct option long_options[] = {
+        {"help", 0, &help, 1},
+        {0, 0, 0, 0}
+    };
 
-        /* grab everything in path till current end */
-        if(!(pdir = strndup(path, end - path)))
-            return -1;
+    help = 0;
+
+    while ((c = getopt_long(argc, argv, "", long_options, NULL)) != -1) {
+        switch (c) {
+            case '?':
+                usage_update();
+                return 1;
+        }
+    }
 
-        /* create the directory ; ignore err if it already exists */
-        ret = mkdir(pdir, mode);
+    if (help) {
+        usage_rebuild();
+        return 0;
+    }
 
-        free(pdir);
+    if (argc > 1) {
+        fprintf(stderr, "bee-dep: too many arguments\n");
+        return 1;
+    }
 
-        if(ret == -1 && errno != EEXIST)
-            return -1;
-  }
+    graph = init_cache();
+    hash_free(graph);
 
-  return 0;
+    return 0;
 }
 
-/*
- * checks if given filename is a regular file
- * returns:
- *
- *   1 : file exists and is a regular file
- *   0 : file is not a regular file or does not exist
- *       check errno for
- *          EEXIST : file exists but is not a regular file
- *          ENOENT : file does not exist
- *  -1 : error; check errno see(stat(2))
- */
-int regular_file_exists(char *fname)
+static int bee_dep_update(int argc, char *argv[])
 {
+    int c, help;
+    char *pkg;
+    char path[PATH_MAX + 1];
+    struct hash *graph;
     struct stat st;
-    int ret;
+    struct option long_options[] = {
+        {"help", 0, &help, 1},
+        {0, 0, 0, 0}
+    };
 
-    ret = stat(fname, &st);
+    help = 0;
 
-    if(likely(ret == 0)) {
-        if(likely(S_ISREG(st.st_mode)))
-            return 1;
+    while ((c = getopt_long(argc, argv, "", long_options, NULL)) != -1) {
+        switch (c) {
+            case '?':
+                usage_update();
+                return 1;
+        }
+    }
 
-        /* set errno for file exists but is not a regular file */
-        errno = EEXIST;
+    if (help) {
+        usage_update();
         return 0;
     }
 
-    if (likely(errno == ENOENT))
+    if (argc == 1) {
+        graph = get_cache();
+
+        if (update_cache(graph) || save_cache(graph, cache_filename())) {
+            hash_free(graph);
+            return 1;
+        }
+
+        hash_free(graph);
         return 0;
+    }
 
-    return -1;
-}
+    if (argc < 2) {
+        fprintf(stderr, "bee-dep: pkgname needed\n");
+        return 1;
+    }
 
-char *get_cachefilename(void)
-{
-    char *cfname;
+    if (argc > 2) {
+        fprintf(stderr, "bee-dep: too many arguments\n");
+        return 1;
+    }
 
-    if(asprintf(&cfname, "%s/%s", BEE_CACHEDIR, CACHENAME) == -1) {
-        perror("bee-dep: get_cachefilename");
-        return NULL;
+    pkg = argv[1];
+
+    if (sprintf(path, "%s/%s/DEPENDENCIES", bee_metadir(), pkg) < 0) {
+        perror("bee-dep: sprintf");
+        return 1;
+    }
+
+    graph = get_cache();
+
+    if (stat(path, &st) != -1) {
+        if (hash_search(graph, pkg)) {
+            fprintf(stderr,
+                    "bee-dep: package \"%s\" is already in the cache\n",
+                    pkg);
+            hash_free(graph);
+            return 0;
+        }
+
+        if (graph_insert_nodes(graph, path)) {
+            hash_free(graph);
+            return 1;
+        }
+    } else {
+        if (remove_package(graph, pkg)) {
+            hash_free(graph);
+            return 1;
+        }
     }
-    return cfname;
+
+    if (save_cache(graph, cache_filename())) {
+        hash_free(graph);
+        return 1;
+    }
+
+    hash_free(graph);
+    return 0;
 }
 
-int do_rebuild_cachedb(void)
+static int bee_dep_remove(int argc, char *argv[])
 {
-    char *cfname;
+    int c, help, print;
     struct hash *graph;
-    int ret;
+    char *pkg;
+    struct option long_options[] = {
+        {"help",  0, &help,  1},
+        {"print", 0, &print, 1},
+        {0, 0, 0, 0}
+    };
+
+    help = print = 0;
 
-    cfname = get_cachefilename();
+    while ((c = getopt_long(argc, argv, "", long_options, NULL)) != -1) {
+        switch (c) {
+            case '?':
+                usage_remove();
+                return 1;
+        }
+    }
 
-    if (!cfname)
+    if (help) {
+        usage_remove();
         return 0;
+    }
 
-    graph = hash_new();
+    if (optind == argc) {
+        fprintf(stderr, "bee-dep: pkgname needed\n");
+        return 1;
+    }
 
-    if (!graph) {
-        free(cfname);
-        return 0;
+    if (optind < argc - 1) {
+        fprintf(stderr, "bee-dep: too many arguments\n");
+        return 1;
     }
 
-    ret = init_cache(graph, cfname);
+    pkg   = argv[optind];
+    graph = get_cache();
 
-    free(cfname);
-    hash_free(graph);
+    if (print && print_removable(graph, pkg)) {
+        hash_free(graph);
+        return 1;
+    }
 
-    return ret;
+    if (remove_package(graph, pkg)
+        || save_cache(graph, cache_filename())) {
+        hash_free(graph);
+        return 1;
+    }
+
+    hash_free(graph);
+    return 0;
 }
 
-int main(int argc, char *argv[])
+static int bee_dep_list(int argc, char *argv[])
 {
-    int c, help, rebuild, update, remove, print, options;
-    int ret;
-    char *cachefile = NULL;
-    char *depfile   = NULL;
-    char found;
-    char *pkgname;
+    int c, i, opt_count, help, files, packages, count,
+        depending_on, required_by, removable, provider_of;
     struct hash *graph;
-    struct stat st;
-    FILE *cache = NULL;
-    struct node *hnode;
-
+    char *name;
     struct option long_options[] = {
-        {"help",            0, &help,    1},
-        {"rebuild",         0, &rebuild, 1},
-        {"update",          0, &update,  1},
-        {"remove",          0, &remove,  1},
-        {"print-removable", 0, &print,   1},
+        {"help",         0, &help,         1},
+        {"files",        0, &files,        1},
+        {"packages",     0, &packages,     1},
+        {"count",        0, &count,        1},
+        {"depending-on", 0, &depending_on, 1},
+        {"required-by",  0, &required_by,  1},
+        {"removable",    0, &removable,    1},
+        {"provider-of",  0, &provider_of,  1},
         {0, 0, 0, 0}
     };
 
-    if (!getenv("BEE_VERSION")) {
-        fprintf(stderr, "BEE-ERROR: please call beedep from bee\n");
-        exit(EXIT_FAILURE);
-    }
-
-    if (argc == 1) {
-        usage();
-        exit(EXIT_FAILURE);
-    }
-
-    help    = rebuild = update = remove = print = options = 0;
-    pkgname = NULL;
+    opt_count = help         = files       = packages  = provider_of =
+    count     = depending_on = required_by = removable = 0;
 
     while ((c = getopt_long(argc, argv, "", long_options, NULL)) != -1) {
         switch (c) {
             case '?':
-                usage();
-                exit(EXIT_FAILURE);
-                break;
+                usage_list();
+                return 1;
 
             default:
-                options++;
+                opt_count++;
         }
     }
 
     if (help) {
-        usage();
-        exit(EXIT_SUCCESS);
+        usage_list();
+        return 0;
     }
 
-    if (argc != optind + !rebuild) {
-        if (argc > optind + !rebuild)
-            fprintf(stderr, "bee-dep: too many arguments\n");
-        else
-            fprintf(stderr, "bee-dep: pkgname needed\n");
+    if (!opt_count && optind != argc) {
+        fprintf(stderr, "bee-dep: too many arguments\n");
+        return 1;
+    }
+
+    if (!opt_count)
+        packages = 1;
 
-        exit(EXIT_FAILURE);
+    if (opt_count > 1 && !count) {
+        fprintf(stderr, "bee-dep: too many options specified\n");
+        return 1;
     }
 
-    if (!rebuild)
-        pkgname = argv[optind];
+    if (packages) {
+        graph = get_cache();
 
-    if (!options) {
-        fprintf(stderr, "bee-dep: no option specified\n");
-        exit(EXIT_FAILURE);
+        if (count)
+            printf("%d\n", count_packages(graph));
+        else
+            list_packages(graph);
+
+        hash_free(graph);
+        return 0;
     }
 
-    if (options > 1 && (options != 2 || !(remove && print))) {
-        fprintf(stderr, "bee-dep: too many options specified\n");
-        exit(EXIT_FAILURE);
+    if (optind == argc) {
+        fprintf(stderr, "bee-dep: arguments needed\n");
+        return 1;
+    }
+
+    if (count && (depending_on || required_by))
+        fprintf(stderr, "bee-dep: ignoring option --count\n");
+
+    graph = get_cache();
+
+    for (i = optind; i < argc; i++) {
+        name = argv[i];
+
+        if (optind < argc - 1)
+            printf("%s:\n", name);
+
+        if (files) {
+            if (count) {
+                c = count_files(graph, name);
+
+                if (c < 0) {
+                    hash_free(graph);
+                    return 1;
+                }
+
+                printf("%d\n", c);
+            } else if (list_files(graph, name)) {
+                 hash_free(graph);
+                 return 1;
+            }
+        }
+
+        if (removable) {
+            if (count) {
+                c = count_removable(graph, name);
+
+                if (c < 0) {
+                    hash_free(graph);
+                    return 1;
+                }
+
+                printf("%d\n", c);
+            } else if (print_removable(graph, name)) {
+                hash_free(graph);
+                return 1;
+            }
+        }
+
+        if (depending_on && print_neededby(graph, name)) {
+            hash_free(graph);
+            return 1;
+        }
+
+        if (required_by && print_needs(graph, name)) {
+            hash_free(graph);
+            return 1;
+        }
+
+        if (provider_of) {
+            if (count) {
+                c = count_providers(graph, name);
+
+                if (c < 0) {
+                    hash_free(graph);
+                    return 1;
+                }
+
+                printf("%d\n", c);
+            } else if (print_providers(graph, name)) {
+                hash_free(graph);
+                return 1;
+            }
+        }
     }
 
-    if (mkdirp(BEE_CACHEDIR, 0755) == -1) {
-        perror("bee-dep: mkdirp");
-        exit(EXIT_FAILURE);
+    hash_free(graph);
+    return 0;
+}
+
+static int bee_dep_conflicts(int argc, char *argv[])
+{
+    int c, opt_count, help;
+    struct hash *graph;
+    struct option long_options[] = {
+        {"help", 0, &help, 1},
+        {0, 0, 0, 0}
+    };
+
+    help = opt_count = 0;
+
+    while ((c = getopt_long(argc, argv, "", long_options, NULL)) != -1) {
+        switch (c) {
+            case '?':
+                usage_conflicts();
+                return 1;
+
+            default:
+                opt_count++;
+        }
     }
 
-    if (rebuild) {
-        if(!do_rebuild_cachedb())
-            return EXIT_FAILURE;
-        return EXIT_SUCCESS;
+    if (help) {
+        usage_conflicts();
+        return 0;
     }
 
-    if(!(graph = hash_new())) {
-        perror("bee-dep: hash_new");
-        exit(EXIT_FAILURE);
+    if (optind < argc) {
+        fprintf(stderr, "bee-dep: too many arguments\n");
+        return 1;
     }
 
-    if(!(cachefile = get_cachefilename())) {
+    graph = get_cache();
+
+    if (print_conflicts(graph)) {
         hash_free(graph);
-        exit(EXIT_FAILURE);
+        return 1;
     }
 
+    hash_free(graph);
+    return 0;
+}
 
-    ret = regular_file_exists(cachefile);
+static int get_command(char *command)
+{
+    if (!strcmp("rebuild", command))
+        return REBUILD;
 
-    if (ret == -1 || (ret == 0 && errno != ENOENT)) {
-        perror("bee-dep: regular_file_exists(cachefile)");
-        free(cachefile);
-        cleanup_and_exit(graph, cache, EXIT_FAILURE);
-    } else if (ret) {
-        cache = open_and_lock(cachefile, "r");
+    if (!strcmp("update", command))
+        return UPDATE;
 
-        if (!load_cache(graph, cache)) {
-            free(cachefile);
-            cleanup_and_exit(graph, cache, EXIT_FAILURE);
-        }
-    } else {
-        if (!init_cache(graph, cachefile)) {
-            free(cachefile);
-            hash_free(graph);
-            return EXIT_FAILURE;
+    if (!strcmp("remove", command))
+        return REMOVE;
+
+    if (!strcmp("list", command))
+        return LIST;
+
+    if (!strcmp("conflicts", command))
+        return CONFLICTS;
+
+    return 0;
+}
+
+static FILE *lockfile(void)
+{
+    static FILE *file = NULL;
+
+    if (!file) {
+        ensure_directories();
+
+        if ((file = fopen(lock_filename(), "w")) == NULL) {
+            perror("bee-dep: lockfile");
+            exit(1);
         }
+
+        fprintf(file, "locked by pid %d\n", getpid());
+        fflush(file);
     }
 
-    hnode = hash_search(graph, pkgname);
-    found = hnode && IS_PKG(hnode);
+    return file;
+}
 
-    if (update) {
-        if (asprintf(&depfile, "%s/%s/DEPENDENCIES",
-                    BEE_METADIR, pkgname) == -1) {
-            perror("bee-dep: asprintf");
-            free(cachefile);
-            cleanup_and_exit(graph, cache, EXIT_FAILURE);
-        }
+void lock(void)
+{
+    FILE *f;
+    struct flock flo;
 
-        if (stat(depfile, &st) != -1) {
-            if (hnode) {
-                fprintf(stderr, "bee-dep: package '%s' is "
-                        "already in the cache\n", pkgname);
-                free(cachefile);
-                free(depfile);
-                cleanup_and_exit(graph, cache, EXIT_SUCCESS);
-            }
+    f = lockfile();
 
-            if (!graph_insert_nodes(graph, depfile)) {
-                free(cachefile);
-                free(depfile);
-                cleanup_and_exit(graph, cache, EXIT_FAILURE);
-            }
-        } else {
-            if (!found) {
-                fprintf(stderr,
-                        "bee-dep: unknown package '%s'\n", pkgname);
-                free(cachefile);
-                free(depfile);
-                cleanup_and_exit(graph, cache, EXIT_FAILURE);
-            }
+    flo.l_start  = flo.l_len = 0;
+    flo.l_whence = SEEK_SET;
+    flo.l_type   = F_WRLCK;
 
-            if (!hnode || !IS_PKG(hnode)) {
-                fprintf(stderr, "bee-dep: unknown package '%s'\n", pkgname);
-                free(cachefile);
-                free(depfile);
-                cleanup_and_exit(graph, cache, EXIT_FAILURE);
-            }
+    if (fcntl(fileno(f), F_SETLKW, &flo) == -1) {
+        perror("bee-dep: lock");
+        exit(1);
+    }
+}
 
-            if (remove_package(graph, pkgname) == EXIT_FAILURE)
-                free(cachefile);
-                free(depfile);
-                cleanup_and_exit(graph, cache, EXIT_FAILURE);
-        }
+void unlock(void)
+{
+    FILE *f;
+    struct flock flo;
 
-        if (!save_cache(graph, cachefile)) {
-            free(cachefile);
-            free(depfile);
-            cleanup_and_exit(graph, cache, EXIT_FAILURE);
-        }
+    f = lockfile();
 
-        free(cachefile);
-        free(depfile);
-        cleanup_and_exit(graph, cache, EXIT_SUCCESS);
+    if (!f)
+        return;
+
+    flo.l_start  = flo.l_len = 0;
+    flo.l_whence = SEEK_SET;
+    flo.l_type   = F_UNLCK;
+
+    rewind(f);
+
+    if (ftruncate(fileno(f), 0) == -1) {
+        perror("bee-dep: unlock: ftruncate");
+        exit(1);
     }
 
-    if (!hnode || !IS_PKG(hnode)) {
-        fprintf(stderr, "bee-dep: unknown package '%s'\n", pkgname);
-        free(cachefile);
-        cleanup_and_exit(graph, cache, EXIT_FAILURE);
+    if (fcntl(fileno(f), F_SETLK, &flo) == -1) {
+        perror("bee-dep: unlock: fcntl");
+        exit(1);
     }
 
-    if (print) {
-        if (print_removable(graph, pkgname) == EXIT_FAILURE) {
-            free(cachefile);
-            cleanup_and_exit(graph, cache, EXIT_FAILURE);
-        }
+    if (fclose(f) == EOF) {
+        perror("bee-dep: unlock: fclose");
+        exit(1);
     }
+}
 
-    if (remove) {
-        if (remove_package(graph, pkgname) == EXIT_FAILURE) {
-            free(cachefile);
-            cleanup_and_exit(graph, cache, EXIT_FAILURE);
-        }
+int main(int argc, char *argv[])
+{
+    int command;
 
-        if (!save_cache(graph, cachefile)) {
-            free(cachefile);
-            cleanup_and_exit(graph, cache, EXIT_FAILURE);
-        }
+    get_bee_variables();
+
+    if (argc < 2) {
+        usage();
+        return 1;
+    }
+
+    lock();
+    if (atexit(unlock)) {
+        perror("bee-dep: atexit");
+        return 1;
     }
 
-    free(cachefile);
-    cleanup_and_exit(graph, cache, EXIT_SUCCESS);
+    command = get_command(argv[1]);
+
+    argv++;
+    argc--;
+
+    switch (command) {
+        case REBUILD:
+            return bee_dep_rebuild(argc, argv);
+
+        case UPDATE:
+            return bee_dep_update(argc, argv);
+
+        case REMOVE:
+            return bee_dep_remove(argc, argv);
+
+        case LIST:
+            return bee_dep_list(argc, argv);
+
+        case CONFLICTS:
+            return bee_dep_conflicts(argc, argv);
+
+        default:
+            usage();
+            return 1;
+    }
 
-    return EXIT_FAILURE;
+    return 0;
 }
diff --git a/src/bee-install.sh.in b/src/bee-install.sh.in
index 5ab420e..1588382 100755
--- a/src/bee-install.sh.in
+++ b/src/bee-install.sh.in
@@ -286,7 +286,7 @@ do_install() {
 
     if [ "${OPT_NOOP}" != "yes" ]; then
         run_hooks post-install ${pkg}
-        ${BEE_LIBEXECDIR}/bee/bee.d/bee-dep --update ${pkg}
+        ${BEE_LIBEXECDIR}/bee/bee.d/bee-dep update ${pkg}
     fi
 
     if [ "${OPT_UPGRADE}" != "yes" ] ; then
diff --git a/src/bee-remove.sh.in b/src/bee-remove.sh.in
index d6d3d83..0ad19dd 100755
--- a/src/bee-remove.sh.in
+++ b/src/bee-remove.sh.in
@@ -60,7 +60,7 @@ pkg_remove() {
 do_remove() {
     pkg=${1}
 
-    FILES=$(${BEE_LIBEXECDIR}/bee/bee.d/bee-dep --remove --print ${pkg##*/})
+    FILES=$(${BEE_LIBEXECDIR}/bee/bee.d/bee-dep remove --print ${pkg##*/})
     if [ $? -ne 0 ] ; then
         echo "removal of ${pkg} failed"
         exit 1
diff --git a/src/graph.c b/src/graph.c
index ca8472e..dc63850 100644
--- a/src/graph.c
+++ b/src/graph.c
@@ -24,17 +24,18 @@
 ** along with this program; if not, see <http://www.gnu.org/licenses/>.
 */
 
-#define _GNU_SOURCE
-
 #include <stdio.h>
 #include <stdlib.h>
 #include <limits.h>
 #include <string.h>
 #include <sys/stat.h>
-#include <unistd.h>
 
 #include "graph.h"
 
+#define IS_FILE(a)       ((a)[0] == '/')
+#define IS_DIR(a)        (!strcmp((a)->type, DIR))
+#define IS_WHITESPACE(a) ((a) == ' ' || (a) == '\t' || (a) == '\n' || (a) == '\r')
+
 static void add_provide(struct node *a, struct node *b)
 {
     tree_insert(a->provide, b);
@@ -88,7 +89,7 @@ char is_virtual_file(char *s)
 int graph_insert_nodes(struct hash *hash, char *filename)
 {
     FILE *file;
-    char *s, *p, *a;
+    register char *s, *p, *a;
     char type_flag, u;
     char line[LINE_MAX],
          prop[LINE_MAX],
@@ -96,11 +97,11 @@ int graph_insert_nodes(struct hash *hash, char *filename)
          pkgname[NODENAME_MAX]  = {0},
          nodename[NODENAME_MAX] = {0};
     int l, line_cnt;
-    struct node *n, *h, *v;
+    struct node *n, *h, *v, *c;
 
     if ((file = fopen(filename, "r")) == NULL) {
         perror("bee-dep: graph_insert_nodes: fopen");
-        return 0;
+        return 1;
     }
 
     line_cnt = type_flag = u = 0;
@@ -137,7 +138,7 @@ int graph_insert_nodes(struct hash *hash, char *filename)
                 fprintf(stderr,
                         "bee-dep: %s: error at line %d: missing bracket\n",
                         filename, line_cnt);
-                return 0;
+                return 1;
             }
 
             *p = '\0';
@@ -146,7 +147,7 @@ int graph_insert_nodes(struct hash *hash, char *filename)
                 fprintf(stderr,
                         "bee-dep: %s: error at line %d: empty node name\n",
                         filename, line_cnt);
-                return 0;
+                return 1;
             }
 
             if (IS_FILE(s)) {
@@ -155,7 +156,7 @@ int graph_insert_nodes(struct hash *hash, char *filename)
                             "bee-dep: %s: error at line %d: "
                             "dont know to which package"
                             "\"%s\" belongs to\n", filename, line_cnt, s);
-                    return 0;
+                    return 1;
                 }
 
                 sprintf(nodename, "%s%s", pkgname, s);
@@ -163,10 +164,10 @@ int graph_insert_nodes(struct hash *hash, char *filename)
                 sprintf(nodename, "%s", s);
             }
 
-            if ((n = hash_search(hash, nodename)) == NULL) {
-                n = node_new(nodename, UNKNOWN);
-                hash_insert(hash, n);
-            }
+            c = node_new(nodename, UNKNOWN);
+            n = hash_safe_insert(hash, c);
+            if (c != n)
+                node_free(c);
 
             type_flag = 0;
             continue;
@@ -192,7 +193,7 @@ int graph_insert_nodes(struct hash *hash, char *filename)
             fprintf(stderr,
                     "bee-dep: %s: error at line %d: missing value "
                     "for property \"%s\"\n", filename, line_cnt, prop);
-            return 0;
+            return 1;
         }
 
         memset(value, '\0', LINE_MAX);
@@ -204,7 +205,7 @@ int graph_insert_nodes(struct hash *hash, char *filename)
                         "bee-dep: %s: error at line %d: "
                         "ambiguous type \"%s\"\n",
                         filename, line_cnt, value);
-                return 0;
+                return 1;
             }
 
             node_set_type(n, value);
@@ -218,18 +219,20 @@ int graph_insert_nodes(struct hash *hash, char *filename)
 
             type_flag = 1;
         } else if (strcasecmp(prop, PROVIDES) == 0) {
-            if ((h = hash_search(hash, value)) == NULL) {
-                h = node_new(value, UNKNOWN);
-                hash_insert(hash, h);
-            }
+            c = node_new(value, UNKNOWN);
+            h = hash_safe_insert(hash, c);
+
+            if (c != h)
+                node_free(c);
 
             if (IS_FILE(value)) {
                 sprintf(nodename, "%s%s", pkgname, value);
 
-                if ((v = hash_search(hash, nodename)) == NULL) {
-                    v = node_new(nodename, UNKNOWN);
-                    hash_insert(hash, v);
-                }
+                c = node_new(nodename, UNKNOWN);
+                v = hash_safe_insert(hash, c);
+
+                if (v != c)
+                    node_free(c);
 
                 add_provide(n, v);
                 add_provide(v, h);
@@ -237,10 +240,11 @@ int graph_insert_nodes(struct hash *hash, char *filename)
                 add_provide(n, h);
             }
         } else if (strcasecmp(prop, NEEDS) == 0) {
-            if ((h = hash_search(hash, value)) == NULL) {
-                h = node_new(value, UNKNOWN);
-                hash_insert(hash, h);
-            }
+            c = node_new(value, UNKNOWN);
+            h = hash_safe_insert(hash, c);
+
+            if (c != h)
+                node_free(c);
 
             add_need(n, h);
         }
@@ -248,18 +252,19 @@ int graph_insert_nodes(struct hash *hash, char *filename)
 
     if (fclose(file) == EOF) {
         perror("bee-dep: graph_insert_nodes: fclose");
-        return 0;
+        return 1;
     }
 
-    if(line_cnt == 0) {
-       fprintf(stderr, "bee-dep: error: file '%s' is empty\n", filename);
-       return 0;
+    if (line_cnt == 0) {
+        fprintf(stderr, "bee-dep: error: file '%s' is empty\n", filename);
+        return 1;
     }
 
-    return 1;
+    return 0;
 }
 
-void search_dependencies(struct hash *hash, struct node *n, struct tree *d)
+static void search_dependencies(struct hash *hash, struct node *n,
+                                struct tree *d)
 {
     struct tree_node *t;
     char *pkgname;
@@ -267,10 +272,13 @@ void search_dependencies(struct hash *hash, struct node *n, struct tree *d)
     t = tree_first(n->neededby->root);
 
     while (t) {
-        pkgname = get_pkgname(t->n->name);
-        if (is_virtual_file(t->n->name))
-            tree_insert(d, hash_search(hash, pkgname));
-        free(pkgname);
+        if (is_virtual_file(t->n->name)) {
+            pkgname = get_pkgname(t->n->name);
+            if (!tree_search_node(d, pkgname))
+                tree_insert(d, hash_search(hash, pkgname));
+            free(pkgname);
+        }
+
         t = tree_next(t);
     }
 
@@ -282,43 +290,27 @@ void search_dependencies(struct hash *hash, struct node *n, struct tree *d)
     }
 }
 
-void print_dependencies(struct hash *hash, char *depend)
+static int print_dependencies(struct hash *hash, struct node *n)
 {
-    struct node *n;
     struct tree *t;
     struct tree_node *e;
 
-    if ((n = hash_search(hash, depend)) == NULL) {
-        fprintf(stderr,
-                "bee-dep: print_dependencies: cannot find \"%s\"\n",
-                depend);
-        return;
-    }
-
-    if (strcmp(n->type, PACKAGE)) {
-        fprintf(stderr,
-                "bee-dep: print_dependencies: \"%s\": no such package\n",
-                depend);
-        return;
-    }
-
     t = tree_new();
 
     search_dependencies(hash, n, t);
 
     e = tree_first(t->root);
 
-    if (!e)
-        puts("none");
-
     while (e) {
-        if (strcmp(depend, e->n->name) != 0)
+        if (strcmp(n->name, e->n->name) != 0)
             puts(e->n->name);
 
         e = tree_next(e);
     }
 
     tree_free(t);
+
+    return 0;
 }
 
 void add_all_neededby(struct hash *hash, struct node *n, struct tree *a)
@@ -446,14 +438,14 @@ int print_removable(struct hash *hash, char *remove)
         fprintf(stderr,
                 "bee-dep: print_removable: cannot find \"%s\"\n",
                 remove);
-        return EXIT_FAILURE;
+        return 1;
     }
 
     if (!IS_PKG(n)) {
         fprintf(stderr,
                 "bee-dep: print_removable: \"%s\": no such package\n",
                 remove);
-        return EXIT_FAILURE;
+        return 1;
     }
 
     t = tree_new();
@@ -465,7 +457,7 @@ int print_removable(struct hash *hash, char *remove)
     if ((dirs = calloc(cnt, sizeof(*dirs))) == NULL
         || (files = calloc(cnt, sizeof(*files))) == NULL) {
         perror("bee-dep: print_removable: calloc");
-        return EXIT_FAILURE;
+        return 1;
     }
 
     e = tree_first(t->root);
@@ -492,27 +484,440 @@ int print_removable(struct hash *hash, char *remove)
     free(files);
     tree_free(t);
 
-    return EXIT_SUCCESS;
+    return 0;
+}
+
+int count_removable(struct hash *hash, char *remove)
+{
+    struct node *n;
+    struct tree *t;
+    int c;
+
+    if ((n = hash_search(hash, remove)) == NULL) {
+        fprintf(stderr,
+                "bee-dep: print_removable: cannot find \"%s\"\n",
+                remove);
+        return -1;
+    }
+
+    if (!IS_PKG(n)) {
+        fprintf(stderr,
+                "bee-dep: print_removable: \"%s\": no such package\n",
+                remove);
+        return -1;
+    }
+
+    t = tree_new();
+
+    search_removable(hash, n, t, remove);
+
+    c = tree_count(t);
+
+    tree_free(t);
+
+    return c;
+}
+
+static void list_all_files(struct node *n)
+{
+    struct tree_node *t;
+
+    t = tree_first(n->provide->root);
+
+    while (t) {
+        if (IS_FILE(t->n->name))
+            puts(t->n->name);
+
+        list_all_files(t->n);
+
+        t = tree_next(t);
+    }
+}
+
+static void count_all_files(struct node *n, int *count)
+{
+    struct tree_node *t;
+
+    t = tree_first(n->provide->root);
+
+    while (t) {
+        if (IS_FILE(t->n->name))
+            (*count)++;
+
+        list_all_files(t->n);
+
+        t = tree_next(t);
+    }
+}
+
+int list_files(struct hash *hash, char *pkgname)
+{
+    struct node *n;
+
+    if ((n = hash_search(hash, pkgname)) == NULL) {
+        fprintf(stderr,
+                "bee-dep: list_files: cannot find \"%s\"\n",
+                pkgname);
+        return 1;
+    }
+
+    if (!IS_PKG(n)) {
+        fprintf(stderr,
+                "bee-dep: list_files: \"%s\": no such package\n",
+                pkgname);
+        return 1;
+    }
+
+    list_all_files(n);
+
+    return 0;
+}
+
+int count_files(struct hash *hash, char *pkgname)
+{
+    struct node *n;
+    int c;
+
+    if ((n = hash_search(hash, pkgname)) == NULL) {
+        fprintf(stderr,
+                "bee-dep: count_files: cannot find \"%s\"\n",
+                pkgname);
+        return -1;
+    }
+
+    if (!IS_PKG(n)) {
+        fprintf(stderr,
+                "bee-dep: count_files: \"%s\": no such package\n",
+                pkgname);
+        return -1;
+    }
+
+    c = 0;
+    count_all_files(n, &c);
+
+    return c;
+}
+
+static void get_all_providers(struct node *n, struct tree *all)
+{
+    struct tree_node *t;
+
+    t = tree_first(n->providedby->root);
+
+    while (t) {
+        if (IS_PKG(t->n) && !tree_search_node(all, t->n->name))
+            tree_insert(all, node_new(t->n->name, ""));
+
+        get_all_providers(t->n, all);
+
+        t = tree_next(t);
+    }
 }
 
-static int save_cache_to_file(struct hash *hash, char *path)
+int print_providers(struct hash *hash, char *name)
+{
+    struct node *n;
+    struct tree_node *t;
+    struct tree *all;
+
+    if ((n = hash_search(hash, name)) == NULL) {
+        fprintf(stderr,
+                "bee-dep: print_providers: cannot find \"%s\"\n",
+                name);
+        return 1;
+    }
+
+    if (IS_PKG(n)) {
+        fprintf(stderr,
+                "bee-dep: print_providers: \"%s\" is a package\n",
+                name);
+        return 1;
+    }
+
+    all = tree_new();
+
+    get_all_providers(n, all);
+
+    t = tree_first(all->root);
+
+    while (t) {
+        puts(t->n->name);
+        t = tree_next(t);
+    }
+
+    tree_free_all_nodes(all);
+    tree_free(all);
+
+    return 0;
+}
+
+int count_providers(struct hash *hash, char *name)
+{
+    int count;
+    struct node *n;
+    struct tree *all;
+
+    count = 0;
+
+    if ((n = hash_search(hash, name)) == NULL) {
+        fprintf(stderr,
+                "bee-dep: count_providers: cannot find \"%s\"\n",
+                name);
+        return -1;
+    }
+
+    if (IS_PKG(n)) {
+        fprintf(stderr,
+                "bee-dep: count_providers: \"%s\" is a package\n",
+                name);
+        return -1;
+    }
+
+    all = tree_new();
+    get_all_providers(n, all);
+
+    count = tree_count(all);
+
+    tree_free_all_nodes(all);
+    tree_free(all);
+
+    return count;
+}
+
+int get_virtual_files(struct node *n, char *name, struct tree *all)
+{
+    struct tree_node *t;
+
+    t = tree_first(n->providedby->root);
+
+    while (t) {
+        if (!strcmp(name, get_filename(t->n->name))
+            && !tree_search_node(all, t->n->name))
+            tree_insert(all, t->n);
+
+        t = tree_next(t);
+    }
+
+    return tree_count(all);
+}
+
+static void search_requirements(struct hash *hash, struct node *n,
+                                struct tree *d)
+{
+    struct tree_node *t, *u;
+    struct tree *providers;
+
+    t = tree_first(n->need->root);
+
+    while (t) {
+        providers = tree_new();
+
+        get_all_providers(t->n, providers);
+
+        u = tree_first(providers->root);
+
+        while (u) {
+            if (!tree_search_node(d, u->n->name))
+                tree_insert(d, hash_search(hash, u->n->name));
+
+            u = tree_next(u);
+        }
+
+        tree_free_all_nodes(providers);
+        tree_free(providers);
+
+        t = tree_next(t);
+    }
+
+    t = tree_first(n->provide->root);
+
+    while (t) {
+        search_requirements(hash, t->n, d);
+        t = tree_next(t);
+    }
+}
+
+static int print_requirements(struct hash *hash, struct node *n)
+{
+    struct tree *t;
+    struct tree_node *e;
+
+    t = tree_new();
+
+    search_requirements(hash, n, t);
+
+    e = tree_first(t->root);
+
+    while (e) {
+        if (strcmp(n->name, e->n->name) != 0)
+            puts(e->n->name);
+
+        e = tree_next(e);
+    }
+
+    tree_free(t);
+
+    return 0;
+}
+
+int print_needs(struct hash *hash, char *name)
+{
+    struct tree_node *t, *v;
+    struct node *n;
+    struct tree *all, *vf;
+    char *pkgname;
+    int count;
+    char p;
+
+    if ((n = hash_search(hash, name)) == NULL) {
+        fprintf(stderr,
+                "bee-dep: print_needs: cannot find \"%s\"\n",
+                name);
+        return 1;
+    }
+
+    if (IS_PKG(n))
+        return print_requirements(hash, n);
+
+    vf    = tree_new();
+    count = 1;
+
+    if (is_virtual_file(n->name))
+        tree_insert(vf, n);
+    else
+        count = get_virtual_files(n, name, vf);
+
+    if (!count) {
+        fprintf(stderr, "bee-dep: could not get virtual file for \"%s\"\n",
+                name);
+        tree_free(vf);
+        return 1;
+    }
+
+    v = tree_first(vf->root);
+
+    while (v) {
+        all = tree_new();
+
+        t = tree_first(v->n->need->root);
+
+        while (t) {
+            get_all_providers(t->n, all);
+            t = tree_next(t);
+        }
+
+        t = tree_first(all->root);
+        p = (t && count > 1);
+
+        if (p) {
+            pkgname = get_pkgname(v->n->name);
+            printf("%s:\n", pkgname);
+            free(pkgname);
+        }
+
+        while (t) {
+            puts(t->n->name);
+            t = tree_next(t);
+        }
+
+        tree_free_all_nodes(all);
+        tree_free(all);
+
+        v = tree_next(v);
+
+        if (v && p)
+            puts("");
+    }
+
+    tree_free(vf);
+
+    return 0;
+}
+
+int print_neededby(struct hash *hash, char *name)
+{
+    struct node *n;
+    struct tree_node *t;
+    struct tree *all;
+
+    if ((n = hash_search(hash, name)) == NULL) {
+        fprintf(stderr,
+                "bee-dep: print_needs: cannot find \"%s\"\n",
+                name);
+        return 1;
+    }
+
+    if (IS_PKG(n))
+        return print_dependencies(hash, n);
+
+    all = tree_new();
+
+    t = tree_first(n->neededby->root);
+
+    while (t) {
+        get_all_providers(t->n, all);
+        t = tree_next(t);
+    }
+
+    t = tree_first(all->root);
+
+    while (t) {
+        puts(t->n->name);
+        t = tree_next(t);
+    }
+
+    tree_free_all_nodes(all);
+    tree_free(all);
+
+    return 0;
+}
+
+void list_packages(struct hash *hash)
 {
     int i;
-    unsigned long index;
-    struct tree_node *s, *t;
-    FILE *file;
+    struct tree_node *t;
 
-    /* TODO: need to handle all kinds of race conditions here 8) */
+    for (i = 0; i < TBLSIZE; i++) {
+        t = tree_first(hash->tbl[i]->root);
 
-    if ((file = fopen(path, "w")) == NULL) {
-        perror("bee-dep: save_cache: fopen");
-        return 0;
+        while (t) {
+            if (IS_PKG(t->n))
+                puts(t->n->name);
+            t = tree_next(t);
+        }
     }
+}
 
-    index = 0;
+int count_packages(struct hash *hash)
+{
+    int i, c;
+    struct tree_node *t;
 
-    if (hash->cnt == 0)
+    c = 0;
+
+    for (i = 0; i < TBLSIZE; i++) {
+        t = tree_first(hash->tbl[i]->root);
+
+        while (t) {
+            if (IS_PKG(t->n))
+                c++;
+            t = tree_next(t);
+        }
+    }
+
+    return c;
+}
+
+int save_cache(struct hash *hash, char *path)
+{
+    int i;
+    struct tree_node *s, *t;
+    FILE *file;
+
+    if ((file = fopen(path, "w")) == NULL) {
+        perror("bee-dep: save_cache: fopen");
         return 1;
+    }
 
     for (i = 0; i < TBLSIZE; i++) {
         if (hash->tbl[i]->root) {
@@ -557,42 +962,13 @@ static int save_cache_to_file(struct hash *hash, char *path)
 
     if (fclose(file) == EOF) {
         perror("bee-dep: save_cache: fclose");
-        return 0;
-    }
-
-    return 1;
-}
-
-int save_cache(struct hash *hash, char *fname)
-{
-    char *tmpname;
-    int ret = 1;
-
-    /* TODO: need to handle all kinds of race conditions here 8) */
-
-    if(asprintf(&tmpname, "%s.tmp", fname) == -1) {
-        perror("bee-dep: save_cache: asprintf");
-        return 0;
-    }
-
-    if(save_cache_to_file(hash, tmpname)) {
-        if(rename(tmpname, fname) == -1) {
-            perror("bee-dep: save_cache: rename");
-            if(unlink(tmpname) == -1) {
-                perror("bee-dep: save_cache: unlink");
-            }
-            ret = 0;
-        }
-    } else {
-        ret = 0;
+        return 1;
     }
 
-    free(tmpname);
-
-    return ret;
+    return 0;
 }
 
-int load_cache(struct hash *hash, FILE *file)
+struct hash *load_cache(char *filename)
 {
     char line[LINE_MAX],
          a[NODENAME_MAX],
@@ -600,8 +976,16 @@ int load_cache(struct hash *hash, FILE *file)
     char c;
     struct node *k, *l;
     int line_cnt;
+    FILE *file;
+    struct hash *hash;
+
+    if ((file = fopen(filename, "r")) == NULL) {
+        perror("bee-dep: load_cache: fopen");
+        return NULL;
+    }
+
+    hash = hash_new();
 
-    rewind(file);
     line_cnt = 0;
 
     while (fgets(line, LINE_MAX, file)) {
@@ -613,7 +997,8 @@ int load_cache(struct hash *hash, FILE *file)
         if (sscanf(line, "%s %s", a, b) == EOF) {
             fprintf(stderr, "beedep: load_cache: "
                             "cache file is broken (line %d)\n", line_cnt);
-            return 0;
+            hash_free(hash);
+            return NULL;
         }
 
         hash_insert(hash, node_new(a, b));
@@ -625,7 +1010,8 @@ int load_cache(struct hash *hash, FILE *file)
         if (sscanf(line, "%s %s %c", a, b, &c) == EOF) {
             fprintf(stderr, "beedep: load_cache: "
                             "cache file is broken (line %d)\n", line_cnt);
-            return 0;
+            hash_free(hash);
+            return NULL;
         }
 
         k = hash_search(hash, a);
@@ -634,7 +1020,8 @@ int load_cache(struct hash *hash, FILE *file)
         if (!k || !l) {
             fprintf(stderr, "beedep: load_cache: "
                             "cache file is broken (line %d)\n", line_cnt);
-            return 0;
+            hash_free(hash);
+            return NULL;
         }
 
         if (c == 'n') {
@@ -646,25 +1033,18 @@ int load_cache(struct hash *hash, FILE *file)
         } else {
             fprintf(stderr, "beedep: load_cache: "
                             "cache file is broken (line %d)\n", line_cnt);
-            return 0;
+            hash_free(hash);
+            return NULL;
         }
     }
 
-    return 1;
-}
-
-unsigned long count_providedby(struct hash *hash, char *count)
-{
-    struct node *n;
-
-    if ((n = hash_search(hash, count)) == NULL) {
-        fprintf(stderr,
-                "bee-dep: count_providedby: cannot find \"%s\"\n",
-                count);
-        exit(EXIT_FAILURE);
+    if (fclose(file) == EOF) {
+        perror("bee-dep: load_cache: fclose");
+        hash_free(hash);
+        return NULL;
     }
 
-    return tree_count(n->providedby);
+    return hash;
 }
 
 void remove_all(struct hash *hash, struct node *n)
@@ -708,17 +1088,66 @@ int remove_package(struct hash *hash, char *pkgname)
         fprintf(stderr,
                 "bee-dep: remove_package: cannot find \"%s\"\n",
                 pkgname);
-        return EXIT_FAILURE;
+        return 1;
     }
 
     if (!IS_PKG(n)) {
         fprintf(stderr,
                 "bee-dep: remove_package: \"%s\": no such package\n",
                 pkgname);
-        return EXIT_FAILURE;
+        return 1;
     }
 
     remove_all(hash, n);
 
-    return EXIT_SUCCESS;
+    return 0;
+}
+
+int print_conflicts(struct hash *hash)
+{
+    int i;
+    struct tree_node *t, *s;
+    char *pkgname;
+
+    for (i = 0; i < TBLSIZE; i++) {
+        t = tree_first(hash->tbl[i]->root);
+
+        while (t) {
+            if (!IS_FILE(t->n->name) || IS_DIR(t->n)
+                || tree_count(t->n->providedby) < 2) {
+                t = tree_next(t);
+                continue;
+            }
+
+            printf("%s: ", t->n->name);
+
+            s = tree_first(t->n->providedby->root);
+
+            while (s) {
+                if (IS_PKG(s->n)) {
+                    printf("%s", s->n->name);
+                } else if (is_virtual_file(s->n->name)) {
+                    pkgname = get_pkgname(s->n->name);
+                    printf("%s", pkgname);
+                    free(pkgname);
+                } else {
+                    fprintf(stderr, "bee-dep: print_conflicts: "
+                            "could not get pkgname for \"%s\"\n",
+                            s->n->name);
+                    return 1;
+                }
+
+                s = tree_next(s);
+
+                if (!s)
+                    puts("");
+                else
+                    printf(" ");
+            }
+
+            t = tree_next(t);
+        }
+    }
+
+    return 0;
 }
diff --git a/src/graph.h b/src/graph.h
index 964b83d..30edf41 100644
--- a/src/graph.h
+++ b/src/graph.h
@@ -35,20 +35,26 @@
 #define DIR     "DIRECTORY"
 #define UNKNOWN "VOID"
 
-#define IS_FILE(a)       ((a)[0] == '/')
-#define IS_DIR(a)        (!strcmp((a)->type, DIR))
-#define IS_WHITESPACE(a) ((a) == ' ' || (a) == '\t' || (a) == '\n' || (a) == '\r')
-#define IS_PKG(a)        (!strcmp((a)->type, PACKAGE))
-
 #include "hash.h"
 
+#define IS_PKG(a) (!strcmp((a)->type, PACKAGE))
+
 extern int graph_insert_nodes(struct hash *hash, char *filename);
-extern void print_dependencies(struct hash *hash, char *depend);
 extern void print_broken(struct hash *hash, char *remove);
 extern int print_removable(struct hash *hash, char *remove);
+extern int count_removable(struct hash *hash, char *remove);
+extern int list_files(struct hash *hash, char *pkgname);
+extern int count_files(struct hash *hash, char *pkgname);
+extern int print_providers(struct hash *hash, char *name);
+extern int count_providers(struct hash *hash, char *name);
+extern int print_needs(struct hash *hash, char *name);
+extern int print_neededby(struct hash *hash, char *name);
+extern void list_packages(struct hash *hash);
+extern int count_packages(struct hash *hash);
 extern int remove_package(struct hash *hash, char *pkgname);
+extern int print_conflicts(struct hash *hash);
 extern int save_cache(struct hash *hash, char *path);
-extern int load_cache(struct hash *hash, FILE *file);
+extern struct hash *load_cache(char *filename);
 extern unsigned long count_providedby(struct hash *hash, char *count);
 
 #endif
diff --git a/src/hash.c b/src/hash.c
index 54101cd..85bed0b 100644
--- a/src/hash.c
+++ b/src/hash.c
@@ -34,8 +34,10 @@ struct hash *hash_new(void)
     struct hash *h;
     unsigned long i;
 
-    if (!(h = calloc(1, sizeof(struct hash))))
-        return NULL;
+    if ((h = calloc(1, sizeof(struct hash))) == NULL) {
+        perror("bee-dep: hash_new: calloc");
+        exit(EXIT_FAILURE);
+    }
 
     for (i = 0; i < TBLSIZE; i++)
         h->tbl[i] = tree_new();
@@ -45,7 +47,7 @@ struct hash *hash_new(void)
 
 unsigned long hash_index(char *key)
 {
-    unsigned long index = 0;
+    register unsigned long index = 0;
     char c;
 
     while ((c = *key++))
@@ -60,8 +62,20 @@ void hash_insert(struct hash *hash, struct node *n)
     unsigned long index = hash_index(n->name);
 
     tree_insert(hash->tbl[index], n);
+}
+
+struct node *hash_safe_insert(struct hash *hash, struct node *n)
+{
+    unsigned long index = hash_index(n->name);
+    struct node *r;
+
+    r = tree_search_node(hash->tbl[index], n->name);
 
-    hash->cnt++;
+    if (r)
+        return r;
+
+    tree_insert(hash->tbl[index], n);
+    return n;
 }
 
 struct node *hash_search(struct hash *hash, char *key)
diff --git a/src/hash.h b/src/hash.h
index c4faf35..f0d0835 100644
--- a/src/hash.h
+++ b/src/hash.h
@@ -29,16 +29,16 @@
 
 #include "beedep_tree.h"
 
-#define TBLSIZE 2000003 /* prime number */
+#define TBLSIZE 2000003L /* prime number */
 
 struct hash {
     struct tree *tbl[TBLSIZE];
-    unsigned long cnt;
 };
 
 extern struct hash *hash_new(void);
 extern unsigned long hash_index(char *key);
 extern void hash_insert(struct hash *hash, struct node *n);
+extern struct node *hash_safe_insert(struct hash *hash, struct node *n);
 extern struct node *hash_search(struct hash *hash, char *key);
 extern void hash_free(struct hash *hash);