diff --git a/src/calc.c b/src/calc.c
index 5f6c9cc9dd3d0af319248b182fb7c3d4fcf95872..451273d9982c368b04acc8d1d4c178f79be70572 100644
--- a/src/calc.c
+++ b/src/calc.c
@@ -172,6 +172,8 @@ int calc_item(struct dir *par, char *name) {
   d->parent = par;
   d->next = par->sub;
   par->sub = d;
+  if(d->next)
+    d->next->prev = d;
   d->name = malloc(strlen(name)+1);
   strcpy(d->name, name);
 
@@ -496,15 +498,14 @@ int calc_process() {
     /* update references and free original item */
     if(orig) {
       root->next = orig->next;
-      if(orig->parent) {
-        t = orig->parent->sub;
-        if(t == orig)
-          orig->parent->sub = root;
-        else if(t != NULL)
-          for(; t->next!=NULL; t=t->next)
-            if(t->next == orig)
-              t->next = root;
-      }
+      root->prev = orig->prev;
+      if(root->parent && root->parent->sub == orig)
+        root->parent->sub = root;
+      if(root->prev)
+        root->prev->next = root;
+      if(root->next)
+        root->next->prev = root;
+      orig->next = orig->prev = NULL;
       freedir(orig);
     }
     browse_init(root->sub);
diff --git a/src/dirlist.c b/src/dirlist.c
index 35ae63467a1af41a80d2a3978f6dffaa29c13595..9e43eec3efb1b97c9a28fa554464f543085a91f6 100644
--- a/src/dirlist.c
+++ b/src/dirlist.c
@@ -122,6 +122,7 @@ struct dir *dirlist_sort(struct dir *list) {
         }
         if(tail) tail->next = e;
         else     list = e;
+        e->prev = tail;
         tail = e;
       }
       p = q;
@@ -225,10 +226,22 @@ struct dir *dirlist_next(struct dir *d) {
 }
 
 
+struct dir *dirlist_prev(struct dir *d) {
+  if(!head || !d)
+    return NULL;
+  while((d = d->prev)) {
+    if(!ISHIDDEN(d))
+      return d;
+  }
+  if(dirlist_parent)
+    return dirlist_parent;
+  return NULL;
+}
+
+
 /* this function assumes that 'selected' is valid and points to a visible item */
 struct dir *dirlist_get(int i) {
   struct dir *t = selected, *d;
-  int j;
 
   if(!head)
     return NULL;
@@ -247,15 +260,15 @@ struct dir *dirlist_get(int i) {
       return t;
   }
 
-  /* negative number? start from beginning to get the index of the selected
-   * item, and then start all over to get the requested item before that.
-   * XXX: This can obviously be optimized by using a doubly linked list. */
-  for(j=0,t=NULL; (t=dirlist_next(t)); j++)
-    if(t == selected)
-      break;
-  for(t=NULL,j+=i; (t=dirlist_next(t))&&j>0; j--)
-    ;
-  return t;
+  /* otherwise, backward */
+  while(1) {
+    d = dirlist_prev(t);
+    if(!d)
+      return t;
+    t = d;
+    if(!++i)
+      return t;
+  }
 }
 
 
diff --git a/src/global.h b/src/global.h
index 66f0107198756193efa4a5bd23e0f840719f687d..e48a58bbdc75eb5ede2634625fb91f9b63bcce92 100644
--- a/src/global.h
+++ b/src/global.h
@@ -52,7 +52,7 @@
  * XXX: probably a good idea to get rid of the custom _t types and use
  *      fixed-size integers instead, which are much more predictable */
 struct dir {
-  struct dir *parent, *next, *sub, *hlnk;
+  struct dir *parent, *next, *prev, *sub, *hlnk;
   char *name;
   off_t size, asize;
   unsigned long items;
diff --git a/src/util.c b/src/util.c
index 6553424f830efd0d7684f3cb8bbfac3b33f28726..a0dc92dd056397efcd71715e1f642395a91f727e 100644
--- a/src/util.c
+++ b/src/util.c
@@ -226,16 +226,12 @@ void freedir(struct dir *dr) {
     freedir_rec(dr->sub);
  
   /* update references */
-  if(dr->parent) {
-    /* item is at the top of the dir, refer to next item */
-    if(dr->parent->sub == dr)
-      dr->parent->sub = dr->next;
-    /* else, get the previous item and update it's "next"-reference */
-    else
-      for(tmp = dr->parent->sub; tmp != NULL; tmp = tmp->next)
-        if(tmp->next == dr)
-          tmp->next = dr->next;
-  }
+  if(dr->parent && dr->parent->sub == dr)
+    dr->parent->sub = dr->next;
+  if(dr->prev)
+    dr->prev->next = dr->next;
+  if(dr->next)
+    dr->next->prev = dr->prev;
 
   freedir_hlnk(dr);