diff --git a/src/browser.c b/src/browser.c index fc27abc46f0ec1a9bf18b9be17824b2e6905f889..77bc4d64c9e87fb334adb23ef30574bf122f3e19 100644 --- a/src/browser.c +++ b/src/browser.c @@ -176,7 +176,7 @@ void browse_draw_item(struct dir *n, int row, off_t max, int ispar) { n->flags & FF_ERR ? '!' : n->flags & FF_SERR ? '.' : n->flags & FF_OTHFS ? '>' : - n->flags & FF_HLNK ? 'H' : + n->flags & FF_HLNKC ? 'H' : !(n->flags & FF_FILE || n->flags & FF_DIR) ? '@' : n->flags & FF_DIR diff --git a/src/calc.c b/src/calc.c index 5f959e93a9926bcc0d63e7edb84a2a7267ebbeca..5af2437ad07e15424ac02cfbdcb1e1fca459dd10 100644 --- a/src/calc.c +++ b/src/calc.c @@ -55,6 +55,9 @@ dev_t curdev; /* current device we're calculating on */ int anpos; /* position of the animation string */ int curpathl = 0, lasterrl = 0; +struct dir **links = NULL; +int linksl, linkst; + /* adds name to curpath */ @@ -84,6 +87,63 @@ void calc_leavepath() { } +/* looks in the links list and adds the file when not found */ +int calc_hlink_add(struct dir *d) { + int i; + /* check the list */ + for(i=0; i<linkst; i++) + if(links[i]->dev == d->dev && links[i]->ino == d->ino) + break; + /* found, do nothing and return the index */ + if(i != linkst) + return i; + /* otherwise, add to list and return -1 */ + if(++linkst > linksl) { + linksl *= 2; + if(!linksl) { + linksl = 64; + links = malloc(linksl*sizeof(struct dir *)); + } else + links = realloc(links, linksl*sizeof(struct dir *)); + } + links[linkst-1] = d; + return -1; +} + + +/* recursively checks a dir structure for hard links and fills the lookup array */ +void calc_hlink_init(struct dir *d) { + struct dir *t; + + for(t=d->sub; t!=NULL; t=t->next) + calc_hlink_init(t); + + if(!(d->flags & FF_HLNKC)) + return; + calc_hlink_add(d); +} + + +/* checks an individual file and updates the flags and cicrular linked list */ +void calc_hlink_check(struct dir *d) { + struct dir *t; + int i; + + d->flags |= FF_HLNKC; + i = calc_hlink_add(d); + + /* found in the list? update hlnk */ + if(i >= 0) { + t = d->hlnk = links[i]; + if(t->hlnk != NULL) + for(t=t->hlnk; t->hlnk!=d->hlnk; t=t->hlnk) + ; + t->hlnk = d; + return; + } +} + + int calc_item(struct dir *par, char *name) { struct dir *t, *d; struct stat fs; @@ -131,11 +191,11 @@ int calc_item(struct dir *par, char *name) { for(t=d->parent; t!=NULL; t=t->parent) t->items++; - /* Provide the necessary information for hard link checking */ + /* 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; + calc_hlink_check(d); /* count the size */ if(!(d->flags & FF_EXL || d->flags & FF_OTHFS)) { @@ -309,6 +369,14 @@ int calc_process() { struct dir *t; int n; + /* create initial links array */ + linksl = linkst = 0; + if(orig) { + for(t=orig; t->parent!=NULL; t=t->parent) + ; + calc_hlink_init(t); + } + /* check root directory */ if((path = path_real(curpath)) == NULL) { failed = 1; @@ -384,6 +452,9 @@ int calc_process() { free(name); free(path); + if(links != NULL) + free(links); + /* success */ if(!n && !failed) { if(root->sub == NULL) { @@ -414,8 +485,6 @@ int calc_process() { } freedir(orig); } - - link_del(root); browse_init(root->sub); return 0; } @@ -436,8 +505,7 @@ calc_fail: void calc_init(char *dir, struct dir *org) { failed = anpos = 0; - if((orig = org) != NULL) - link_add(orig); + orig = org; if(curpathl == 0) { curpathl = strlen(dir)+1; curpath = malloc(curpathl); diff --git a/src/delete.c b/src/delete.c index 55265130feb2dae4a5fcf1ac3eea10e19c6dd627..cc44d2401066d9d2441ac5c3120762dd82098dfc 100644 --- a/src/delete.c +++ b/src/delete.c @@ -222,19 +222,13 @@ void delete_process() { return; } - /* 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)) { - link_del(root); + if(input_handle(0)) return; - } } /* delete */ @@ -247,8 +241,6 @@ void delete_process() { nextsel->flags |= FF_BSEL; browse_init(n); } - if(n != NULL) - link_del(n); } diff --git a/src/global.h b/src/global.h index f58ccb57954c26fe935f3236746c1b007a97d67e..47dcbe3902bf9a8dce92baad023c2c381c8a5636 100644 --- a/src/global.h +++ b/src/global.h @@ -38,9 +38,8 @@ #define FF_OTHFS 0x08 /* excluded because it was an other filesystem */ #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_HLNKC 0x80 /* hard link candidate (file with st_nlink > 1) */ -#define FF_BSEL 0x100 /* selected */ +#define FF_HLNKC 0x40 /* hard link candidate (file with st_nlink > 1) */ +#define FF_BSEL 0x80 /* selected */ /* Program states */ #define ST_CALC 0 @@ -51,11 +50,11 @@ /* structure representing a file or directory */ struct dir { - struct dir *parent, *next, *sub; + struct dir *parent, *next, *sub, *hlnk; char *name; off_t size, asize; unsigned long items; - unsigned short flags; + unsigned char flags; dev_t dev; ino_t ino; }; diff --git a/src/util.c b/src/util.c index 9c9a6c1004503e53c7ddd368f0ceacdce01b006e..8980c2fb174a2d23371af57188da66e3550e4403 100644 --- a/src/util.c +++ b/src/util.c @@ -169,10 +169,23 @@ void ncprint(int r, int c, char *fmt, ...) { } +/* removes item from the hlnk circular linked list */ +void freedir_hlnk(struct dir *d) { + struct dir *t; + if(!d->hlnk) + return; + for(t=d->hlnk; t->hlnk!=d; t=t->hlnk) + ; + t->hlnk = d->hlnk; +} + + void freedir_rec(struct dir *dr) { struct dir *tmp, *tmp2; tmp2 = dr; while((tmp = tmp2) != NULL) { + freedir_hlnk(tmp); + /* remove item */ if(tmp->sub) freedir_rec(tmp->sub); free(tmp->name); tmp2 = tmp->next; @@ -208,6 +221,7 @@ void freedir(struct dir *dr) { tmp->next = dr->next; } + freedir_hlnk(dr); free(dr->name); free(dr); } @@ -249,81 +263,3 @@ 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 152a08ad72e4895ee4f9e8e0bf6a07f1390d9892..fc7e29b985e3f621e0dc3f7e5f36a8e20042d449 100644 --- a/src/util.h +++ b/src/util.h @@ -78,11 +78,5 @@ 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 link_del(struct dir *); - -/* re-adds all hard links in a tree */ -void link_add(struct dir *); - #endif