diff --git a/ChangeLog b/ChangeLog
index 18b2d278ca8f89b90a3c0c24aed6f57fc382d541..e78d55130b4dcdd8d4da2f89a1b57560ed530247 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,6 @@
+git - ?
+  - Implemented hard link detection
+
 1.5 - 2009-05-02
 	- Fixed incorrect apparent size on directory refresh
 	- Browsing keys now work while file info window is displayed
diff --git a/src/calc.c b/src/calc.c
index 0c05a0501d8724a397e59fdcab2aa002d8d672be..a09ab6cf5e2bca81c5bc5da4af7bbe51bd825ba4 100644
--- a/src/calc.c
+++ b/src/calc.c
@@ -53,11 +53,7 @@ struct dir *root;        /* root directory struct we're calculating */
 struct dir *orig;        /* original directory, when recalculating */
 dev_t curdev;            /* current device we're calculating on */
 int anpos;               /* position of the animation string */
-struct link_inode {      /* list of all non-dirs with nlink > 1 */
-  dev_t dev;
-  ino_t ino;
-} *links = NULL;
-int curpathl = 0, lasterrl = 0, linksl = 0, linkst = 0;
+int curpathl = 0, lasterrl = 0;
 
 
 
@@ -136,33 +132,14 @@ int calc_item(struct dir *par, char *name) {
     for(t=d->parent; t!=NULL; t=t->parent)
       t->items++;
 
-  /* check for hard links.
-     An item is only considered a hard link if it's not a directory,
-     has st_nlink > 1, and is already present in the links array */
-  if(!S_ISDIR(fs.st_mode) && fs.st_nlink > 1) {
-    for(i=0; i<linkst; i++)
-      if(links[i].dev == fs.st_dev && links[i].ino == fs.st_ino)
-        break;
-    /* found in the list, set link flag (so the size won't get counted) */
-    if(i != linkst)
-      d->flags |= FF_HLNK;
-    /* not found, add to the list */
-    else {
-      if(++linkst > linksl) {
-        linksl *= 2;
-        if(!linksl) {
-          linksl = 64;
-          links = malloc(linksl*sizeof(struct link_inode));
-        } else
-          links = realloc(links, linksl*sizeof(struct link_inode));
-      }
-      links[i].dev = fs.st_dev;
-      links[i].ino = fs.st_ino;
-    }
-  }
+  /* Provide the necessary information for hard link checking */
+  d->ino = fs.st_ino;
+  d->dev = fs.st_dev;
+  if(!S_ISDIR(fs.st_mode) && fs.st_nlink > 1)
+    d->flags |= FF_HLNKC;
 
   /* count the size */
-  if(!(d->flags & FF_EXL || d->flags & FF_OTHFS || d->flags & FF_HLNK)) {
+  if(!(d->flags & FF_EXL || d->flags & FF_OTHFS)) {
     d->size = fs.st_blocks * S_BLKSIZE;
     d->asize = fs.st_size;
     for(t=d->parent; t!=NULL; t=t->parent) {
@@ -404,10 +381,6 @@ void calc_process() {
   if(!path[1] && strcmp(name, "."))
     free(name);
   free(path);
-  if(linksl) {
-    linksl = linkst = 0;
-    free(links);
-  }
 
   /* success */
   if(!n && !failed) {
@@ -417,7 +390,6 @@ void calc_process() {
       strcpy(errmsg, "Directory empty.");
       goto calc_fail;
     }
-    browse_init(root->sub);
 
     /* update references and free original item */
     if(orig) {
@@ -440,6 +412,9 @@ void calc_process() {
       }
       freedir(orig);
     }
+
+    link_del(root);
+    browse_init(root->sub);
     return;
   }
 
@@ -455,7 +430,8 @@ calc_fail:
 
 void calc_init(char *dir, struct dir *org) {
   failed = anpos = 0;
-  orig = org;
+  if((orig = org) != NULL)
+    link_add(orig);
   if(curpathl == 0) {
     curpathl = strlen(dir)+1;
     curpath = malloc(curpathl);
diff --git a/src/delete.c b/src/delete.c
index a5b5b2dde58f488b67f54e250b3bc60e2ddee706..8f5e30cc30301522cd0b394aa44832efe2d5f8b7 100644
--- a/src/delete.c
+++ b/src/delete.c
@@ -216,13 +216,19 @@ void delete_process() {
     if(input_handle(0))
       return browse_init(root);
 
+  /* temporarily re-add hard links, so we won't lose sizes in case we delete
+     a file of which another file outside this directory was marked as duplicate */
+  link_add(root);
+
   /* chdir */
   if(path_chdir(getpath(root->parent)) < 0) {
     state = DS_FAILED;
     lasterrno = errno;
     while(state == DS_FAILED)
-      if(input_handle(0))
+      if(input_handle(0)) {
+        link_del(root);
         return;
+      }
   }
 
   /* delete */
@@ -235,6 +241,7 @@ void delete_process() {
     if(nextsel)
       nextsel->flags |= FF_BSEL;
   }
+  link_del(root);
 }
 
 
diff --git a/src/global.h b/src/global.h
index dfa7ff6a3f15b0451f3fdff8de97184d8102eeb3..43bbb3ffbe14e0c8350584e031e959f3390dfa71 100644
--- a/src/global.h
+++ b/src/global.h
@@ -29,6 +29,7 @@
 #include "config.h"
 #include <stdio.h>
 #include <sys/types.h>
+#include <sys/stat.h>
 
 /* File Flags (struct dir -> flags) */
 #define FF_DIR    0x01
@@ -38,7 +39,8 @@
 #define FF_EXL    0x10 /* excluded using exlude patterns */
 #define FF_SERR   0x20 /* error in subdirectory */
 #define FF_HLNK   0x40 /* hard link (same file already encountered before) */
-#define FF_BSEL   0x80 /* selected */
+#define FF_HLNKC  0x80 /* hard link candidate (file with st_nlink > 1) */
+#define FF_BSEL  0x100 /* selected */
 
 /* Program states */
 #define ST_CALC   0
@@ -54,7 +56,9 @@ struct dir {
   char *name;
   off_t size, asize;
   unsigned long items;
-  unsigned char flags;
+  unsigned short flags;
+  dev_t dev;
+  ino_t ino;
 }; 
 
 /* program state */
diff --git a/src/util.c b/src/util.c
index a19fea0790f09463b39173e7e7e7ef89221f0f18..6c95ba00bc77d3e9cac789172dc8734ceb7d0d38 100644
--- a/src/util.c
+++ b/src/util.c
@@ -38,6 +38,9 @@ char fullsizedat[20]; /* max: 999.999.999.999.999 */
 char *getpathdat;
 int getpathdatl = 0;
 
+struct dir **links;
+int linksl = 0, linkst = 0;
+
 
 char *cropstr(const char *from, int s) {
   int i, j, o = strlen(from);
@@ -246,3 +249,81 @@ char *getpath(struct dir *cur) {
   return getpathdat;
 }
 
+
+/* act =  0  -> just fill the links array
+   act =  1  -> fill array and remove duplicates
+   act = -1  -> use array to re-add duplicates */
+void link_list_rec(struct dir *d, int act) {
+  struct dir *t;
+  int i;
+
+  /* recursion, check sub directories */
+  for(t=d->sub; t!=NULL; t=t->next)
+    link_list_rec(t, act);
+
+  /* not a link candidate? ignore */
+  if(!(d->flags & FF_HLNKC))
+    return;
+
+  /* check against what we've found so far */
+  for(i=0; i<linkst; i++)
+    if(links[i]->dev == d->dev && links[i]->ino == d->ino)
+      break;
+
+  /* found in the list, set link flag and set size to zero */
+  if(act == 1 && i != linkst) {
+    d->flags |= FF_HLNK;
+    for(t=d->parent; t!=NULL; t=t->parent) {
+      t->size -= d->size;
+      t->asize -= d->asize;
+    }
+    d->size = d->asize = 0;
+    return;
+  }
+
+  /* found in the list, reset flag and re-add size */
+  if(act == -1 && i != linkst && d->flags & FF_HLNK) {
+    d->flags -= FF_HLNK;
+    d->size = links[i]->size;
+    d->asize = links[i]->asize;
+    for(t=d->parent; t!=NULL; t=t->parent) {
+      t->size += d->size;
+      t->asize += d->asize;
+    }
+  }
+
+  /* not found, add to the list */
+  if(act == 1 || (act == 0 && !(d->flags & FF_HLNK))) {
+    if(++linkst > linksl) {
+      linksl *= 2;
+      if(!linksl) {
+        linksl = 64;
+        links = malloc(linksl*sizeof(struct dir *));
+      } else
+        links = realloc(links, linksl*sizeof(struct dir *));
+    }
+    links[i] = d;
+  }
+}
+
+
+void link_del(struct dir *par) {
+  while(par->parent != NULL)
+    par = par->parent;
+  link_list_rec(par, 1);
+  linkst = 0;
+}
+
+
+void link_add(struct dir *par) {
+  while(par->parent != NULL)
+    par = par->parent;
+  /* In order to correctly re-add the duplicates, we'll have to pass the entire
+     tree twice, one time to get a list of all links, second time to re-add them */
+  link_list_rec(par, 0);
+  link_list_rec(par, -1);
+  linkst = 0;
+}
+
+
+
diff --git a/src/util.h b/src/util.h
index e83c25e2aa07b78d5f2c868667c7ad311026ab3f..95cf7e14277d95440f5391f6393454a89a3f4fbb 100644
--- a/src/util.h
+++ b/src/util.h
@@ -78,5 +78,11 @@ void freedir(struct dir *);
    returned pointer will be overwritten with a subsequent call */
 char *getpath(struct dir *);
 
+/* removes all hard links from a tree */
+void links_del(struct dir *);
+
+/* re-adds all hard links in a tree */
+void link_add(struct dir *);
+
 #endif