diff options
author | DongHun Kwak <dh0128.kwak@samsung.com> | 2021-10-25 10:41:31 +0900 |
---|---|---|
committer | DongHun Kwak <dh0128.kwak@samsung.com> | 2021-10-25 10:41:31 +0900 |
commit | b84ead28bd5ff6581cda4511883c97301e541010 (patch) | |
tree | fc95b731f19de20eab6eadf1bb782c8e26c39a12 | |
parent | cbf23e20b0a41e2d5384515eb6ecc6a50a8aab3f (diff) | |
download | fdupes-b84ead28bd5ff6581cda4511883c97301e541010.tar.gz fdupes-b84ead28bd5ff6581cda4511883c97301e541010.tar.bz2 fdupes-b84ead28bd5ff6581cda4511883c97301e541010.zip |
Imported Upstream version 2.1.0upstream/2.1.0
-rw-r--r-- | CHANGES | 19 | ||||
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | README | 4 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | fdupes-help.7 | 14 | ||||
-rw-r--r-- | fdupes.c | 4 | ||||
-rw-r--r-- | ncurses-commands.c | 203 | ||||
-rw-r--r-- | ncurses-commands.h | 2 | ||||
-rw-r--r-- | ncurses-interface.c | 187 |
9 files changed, 240 insertions, 197 deletions
@@ -6,6 +6,25 @@ who contributed the patch or idea appears first, followed by those who've otherwise worked on that item. For a list of contributors names and identifiers please see the CONTRIBUTORS file. +Changes from 2.0.0 to 2.1.0: + + - Rename cs command ("clear all selections") from cs to csel. + - Rename igs command ("invert selections") from igs to isel. + - Add "prune" command as synonym for DELETE key. + - Clear selections after deleting files via prune/DELETE. + - Fix dependency issues when fdupes is configured to not use ncurses. + +Changes from 1.6.1 to 2.0.0: + +- Add ncurses mode for interactive file deletion (plain mode still available + via --plain or ./configure). +- Add --minsize option. +- Add --maxsize option. +- Add --time option. +- Add --order=ctime option. +- Add --log option. +- Use configure script for installation (Autotools/Automake). + Changes from 1.6.0 to 1.6.1: - Fix 'invalid option' error for -I. [AL] diff --git a/Makefile.am b/Makefile.am index ecb5087..18ffdbf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -17,8 +17,6 @@ fdupes_SOURCES = fdupes.c\ flags.h\ mbstowcs_escape_invalid.c\ mbstowcs_escape_invalid.h\ - positive_wcwidth.c\ - positive_wcwidth.h\ md5/md5.c\ md5/md5.h dist_man1_MANS = fdupes.1 @@ -17,8 +17,8 @@ Usage: fdupes [options] DIRECTORY... -H --hardlinks normally, when two or more files point to the same disk area they are treated as non-duplicates; this option will change this behavior - -G --minsize=SIZE consider only files greater than or equal to SIZE - -L --maxsize=SIZE consider only files less than or equal to SIZE + -G --minsize=SIZE consider only files greater than or equal to SIZE in bytes + -L --maxsize=SIZE consider only files less than or equal to SIZE in bytes -n --noempty exclude zero-length files from consideration -A --nohidden exclude hidden files from consideration -f --omitfirst omit the first file in each set of matches diff --git a/configure.ac b/configure.ac index a250f83..1ee4308 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -AC_INIT([fdupes], [2.0.0]) +AC_INIT([fdupes], [2.1.0]) AM_INIT_AUTOMAKE([foreign subdir-objects]) diff --git a/fdupes-help.7 b/fdupes-help.7 index 51a1349..42d9df0 100644 --- a/fdupes-help.7 +++ b/fdupes-help.7 @@ -181,14 +181,14 @@ regular expression (see below). .B -.IP "'cs'" +.IP "'csel'" Clear all selections. .B -.IP "'igs'" +.IP "'isel'" Invert selections within selected sets. For example, if files 1 and 4 in a set of 5 are selected, .B -igs +isel will deselect files 1 and 4, and select files 2, 3, and 5. Immediately repeating the same command will deselect files 2, 3, and 5, and select files 1 and 4, restoring selections to their previous state. .SH "TAGGING SELECTED FILES" @@ -207,10 +207,12 @@ Tag selected files for deletion. Remove all tags from selected files. .SH "DELETING DUPLICATES" -Once they've been tagged for deletion, files can be deleted by pressing +Once tagged for deletion, files can be deleted by pressing .B -DELETE. -Fdupes will delete any files that are tagged for deletion and delist any sets whose remaining files have been tagged for keeping. For safety, fdupes will refuse to act on sets for which all files have been tagged for deletion. To handle these cases, tag at least one file for keeping and run the delete command again. +DELETE +or using the +.B 'prune' +command. Fdupes will delete any files that are tagged for deletion and delist any sets whose remaining files have been tagged for keeping. For safety, fdupes will refuse to act on sets for which all files have been tagged for deletion. To handle these cases, tag at least one file for keeping and run the delete command again. .SH "OTHER COMMANDS" .B @@ -35,14 +35,16 @@ #include <errno.h> #include <libgen.h> #include <locale.h> +#ifndef NO_NCURSES #ifdef HAVE_NCURSESW_CURSES_H #include <ncursesw/curses.h> #else #include <curses.h> #endif +#include "ncurses-interface.h" +#endif #include "fdupes.h" #include "errormsg.h" -#include "ncurses-interface.h" #include "log.h" #include "sigint.h" #include "flags.h" diff --git a/ncurses-commands.c b/ncurses-commands.c index d00d58e..1601c70 100644 --- a/ncurses-commands.c +++ b/ncurses-commands.c @@ -24,6 +24,7 @@ #include "ncurses-commands.h" #include "wcs.h" #include "mbstowcs_escape_invalid.h" +#include "log.h" #include <wchar.h> #include <pcre2.h> @@ -40,14 +41,15 @@ struct command_map command_list[] = { {L"dsele", COMMAND_CLEAR_SELECTIONS_ENDING}, {L"dselm", COMMAND_CLEAR_SELECTIONS_MATCHING}, {L"dselr", COMMAND_CLEAR_SELECTIONS_REGEX}, - {L"cs", COMMAND_CLEAR_ALL_SELECTIONS}, - {L"igs", COMMAND_INVERT_GROUP_SELECTIONS}, + {L"csel", COMMAND_CLEAR_ALL_SELECTIONS}, + {L"isel", COMMAND_INVERT_GROUP_SELECTIONS}, {L"ks", COMMAND_KEEP_SELECTED}, {L"ds", COMMAND_DELETE_SELECTED}, {L"rs", COMMAND_RESET_SELECTED}, {L"rg", COMMAND_RESET_GROUP}, {L"all", COMMAND_PRESERVE_ALL}, {L"goto", COMMAND_GOTO_SET}, + {L"prune", COMMAND_PRUNE}, {L"exit", COMMAND_EXIT}, {L"quit", COMMAND_EXIT}, {L"help", COMMAND_HELP}, @@ -569,7 +571,8 @@ int cmd_clear_all_selections(struct filegroup *groups, int groupcount, wchar_t * groups[g].selected = 0; } - format_status_left(status, L"Cleared all selections."); + if (status) + format_status_left(status, L"Cleared all selections."); return 1; } @@ -674,3 +677,197 @@ int cmd_reset_selected(struct filegroup *groups, int groupcount, wchar_t *comman return 1; } + +int filerowcount(file_t *file, const int columns, int group_file_count); + +/* delete files tagged for deletion, delist sets with no untagged files */ +int cmd_prune(struct filegroup *groups, int groupcount, wchar_t *commandarguments, size_t *deletiontally, int *totalgroups, int *cursorgroup, int *cursorfile, int *topline, char *logfile, WINDOW *filewin, struct status_text *status) +{ + int deletecount; + int preservecount; + int unresolvedcount; + int totaldeleted = 0; + double deletedbytes = 0; + struct log_info *loginfo; + int g; + int f; + int to; + int adjusttopline; + int toplineoffset; + int groupfirstline; + + if (logfile != 0) + loginfo = log_open(logfile, 0); + else + loginfo = 0; + + for (g = 0; g < *totalgroups; ++g) + { + preservecount = 0; + deletecount = 0; + unresolvedcount = 0; + + for (f = 0; f < groups[g].filecount; ++f) + { + switch (groups[g].files[f].action) + { + case -1: + ++deletecount; + break; + case 0: + ++unresolvedcount; + break; + case 1: + ++preservecount; + break; + } + } + + if (loginfo) + log_begin_set(loginfo); + + /* delete files marked for deletion unless no files left undeleted */ + if (deletecount < groups[g].filecount) + { + for (f = 0; f < groups[g].filecount; ++f) + { + if (groups[g].files[f].action == -1) + { + if (remove(groups[g].files[f].file->d_name) == 0) + { + set_file_action(&groups[g].files[f], -2, deletiontally); + + deletedbytes += groups[g].files[f].file->size; + ++totaldeleted; + + if (loginfo) + log_file_deleted(loginfo, groups[g].files[f].file->d_name); + } + } + } + + if (loginfo) + { + for (f = 0; f < groups[g].filecount; ++f) + { + if (groups[g].files[f].action >= 0) + log_file_remaining(loginfo, groups[g].files[f].file->d_name); + } + } + + deletecount = 0; + } + + if (loginfo) + log_end_set(loginfo); + + /* if no files left unresolved, mark preserved files for delisting */ + if (unresolvedcount == 0) + { + for (f = 0; f < groups[g].filecount; ++f) + if (groups[g].files[f].action == 1) + set_file_action(&groups[g].files[f], -2, deletiontally); + + preservecount = 0; + } + /* if only one file left unresolved, mark it for delesting */ + else if (unresolvedcount == 1 && preservecount + deletecount == 0) + { + for (f = 0; f < groups[g].filecount; ++f) + if (groups[g].files[f].action == 0) + set_file_action(&groups[g].files[f], -2, deletiontally); + } + + /* delist any files marked for delisting */ + to = 0; + for (f = 0; f < groups[g].filecount; ++f) + if (groups[g].files[f].action != -2) + groups[g].files[to++] = groups[g].files[f]; + + groups[g].filecount = to; + + /* reposition cursor, if necessary */ + if (*cursorgroup == g && *cursorfile > 0 && *cursorfile >= groups[g].filecount) + *cursorfile = groups[g].filecount - 1; + } + + if (loginfo != 0) + log_close(loginfo); + + if (deletedbytes < 1000.0) + format_status_left(status, L"Deleted %ld files (occupying %.0f bytes).", totaldeleted, deletedbytes); + else if (deletedbytes <= (1000.0 * 1000.0)) + format_status_left(status, L"Deleted %ld files (occupying %.1f KB).", totaldeleted, deletedbytes / 1000.0); + else if (deletedbytes <= (1000.0 * 1000.0 * 1000.0)) + format_status_left(status, L"Deleted %ld files (occupying %.1f MB).", totaldeleted, deletedbytes / (1000.0 * 1000.0)); + else + format_status_left(status, L"Deleted %ld files (occupying %.1f GB).", totaldeleted, deletedbytes / (1000.0 * 1000.0 * 1000.0)); + + /* delist empty groups */ + to = 0; + for (g = 0; g < *totalgroups; ++g) + { + if (groups[g].filecount > 0) + { + groups[to] = groups[g]; + + /* reposition cursor, if necessary */ + if (to == *cursorgroup && to != g) + *cursorfile = 0; + + ++to; + } + else + { + free(groups[g].files); + } + } + + *totalgroups = to; + + /* reposition cursor, if necessary */ + if (*cursorgroup >= *totalgroups) + { + *cursorgroup = *totalgroups - 1; + *cursorfile = 0; + } + + /* recalculate line boundaries */ + adjusttopline = 1; + toplineoffset = 0; + groupfirstline = 0; + + for (g = 0; g < *totalgroups; ++g) + { + if (adjusttopline && groups[g].endline >= *topline) + toplineoffset = groups[g].endline - *topline; + + groups[g].startline = groupfirstline; + groups[g].endline = groupfirstline + 2; + + for (f = 0; f < groups[g].filecount; ++f) + groups[g].endline += filerowcount(groups[g].files[f].file, COLS, groups[g].filecount); + + if (adjusttopline && toplineoffset > 0) + { + *topline = groups[g].endline - toplineoffset; + + if (*topline < 0) + *topline = 0; + + adjusttopline = 0; + } + + groupfirstline = groups[g].endline + 1; + } + + if (*totalgroups > 0 && groups[*totalgroups-1].endline <= *topline) + { + *topline = groups[*totalgroups-1].endline - getmaxy(filewin) + 1; + + if (*topline < 0) + *topline = 0; + } + + cmd_clear_all_selections(groups, *totalgroups, commandarguments, 0); +}
\ No newline at end of file diff --git a/ncurses-commands.h b/ncurses-commands.h index e0fca68..9e5ebd2 100644 --- a/ncurses-commands.h +++ b/ncurses-commands.h @@ -48,6 +48,7 @@ #define COMMAND_SELECT_REGEX 20 #define COMMAND_CLEAR_SELECTIONS_REGEX 21 #define COMMAND_GOTO_SET 22 +#define COMMAND_PRUNE 23 extern struct command_map command_list[]; extern struct command_map confirmation_keyword_list[]; @@ -70,5 +71,6 @@ int cmd_invert_group_selections(struct filegroup *groups, int groupcount, wchar_ int cmd_keep_selected(struct filegroup *groups, int groupcount, wchar_t *commandarguments, size_t *deletiontally, struct status_text *status); int cmd_delete_selected(struct filegroup *groups, int groupcount, wchar_t *commandarguments, size_t *deletiontally, struct status_text *status); int cmd_reset_selected(struct filegroup *groups, int groupcount, wchar_t *commandarguments, size_t *deletiontally, struct status_text *status); +int cmd_prune(struct filegroup *groups, int groupcount, wchar_t *commandarguments, size_t *deletiontally, int *totalgroups, int *cursorgroup, int *cursorfile, int *topline, char *logfile, WINDOW *filewin, struct status_text *status);; #endif
\ No newline at end of file diff --git a/ncurses-interface.c b/ncurses-interface.c index 8129ed2..6c80f7f 100644 --- a/ncurses-interface.c +++ b/ncurses-interface.c @@ -376,9 +376,7 @@ void deletefiles_ncurses(file_t *files, char *logfile) int preservecount; int deletecount; int unresolvedcount; - int totaldeleted; size_t globaldeletiontally = 0; - double deletedbytes; int row; int x; int g; @@ -386,7 +384,6 @@ void deletefiles_ncurses(file_t *files, char *logfile) int keyresult; int cy; int f; - int to; wchar_t *commandbuffer; size_t commandbuffersize; wchar_t *commandarguments; @@ -401,12 +398,9 @@ void deletefiles_ncurses(file_t *files, char *logfile) struct prompt_info *prompt; int dupesfound; int intresult; - int adjusttopline; - int toplineoffset = 0; int resumecommandinput = 0; int index_width; int timestamp_width; - struct log_info *loginfo; noecho(); cbreak(); @@ -884,6 +878,10 @@ void deletefiles_ncurses(file_t *files, char *logfile) break; + case COMMAND_PRUNE: + cmd_prune(groups, totalgroups, commandarguments, &globaldeletiontally, &totalgroups, &cursorgroup, &cursorfile, &topline, logfile, filewin, status); + break; + case COMMAND_EXIT: /* exit program */ if (totalgroups == 0) { @@ -1231,182 +1229,7 @@ void deletefiles_ncurses(file_t *files, char *logfile) break; case KEY_DC: - totaldeleted = 0; - deletedbytes = 0; - - if (logfile != 0) - loginfo = log_open(logfile, 0); - else - loginfo = 0; - - for (g = 0; g < totalgroups; ++g) - { - preservecount = 0; - deletecount = 0; - unresolvedcount = 0; - - for (f = 0; f < groups[g].filecount; ++f) - { - switch (groups[g].files[f].action) - { - case -1: - ++deletecount; - break; - case 0: - ++unresolvedcount; - break; - case 1: - ++preservecount; - break; - } - } - - if (loginfo) - log_begin_set(loginfo); - - /* delete files marked for deletion unless no files left undeleted */ - if (deletecount < groups[g].filecount) - { - for (f = 0; f < groups[g].filecount; ++f) - { - if (groups[g].files[f].action == -1) - { - if (remove(groups[g].files[f].file->d_name) == 0) - { - set_file_action(&groups[g].files[f], -2, &globaldeletiontally); - - deletedbytes += groups[g].files[f].file->size; - ++totaldeleted; - - if (loginfo) - log_file_deleted(loginfo, groups[g].files[f].file->d_name); - } - } - } - - if (loginfo) - { - for (f = 0; f < groups[g].filecount; ++f) - { - if (groups[g].files[f].action >= 0) - log_file_remaining(loginfo, groups[g].files[f].file->d_name); - } - } - - deletecount = 0; - } - - if (loginfo) - log_end_set(loginfo); - - /* if no files left unresolved, mark preserved files for delisting */ - if (unresolvedcount == 0) - { - for (f = 0; f < groups[g].filecount; ++f) - if (groups[g].files[f].action == 1) - set_file_action(&groups[g].files[f], -2, &globaldeletiontally); - - preservecount = 0; - } - /* if only one file left unresolved, mark it for delesting */ - else if (unresolvedcount == 1 && preservecount + deletecount == 0) - { - for (f = 0; f < groups[g].filecount; ++f) - if (groups[g].files[f].action == 0) - set_file_action(&groups[g].files[f], -2, &globaldeletiontally); - } - - /* delist any files marked for delisting */ - to = 0; - for (f = 0; f < groups[g].filecount; ++f) - if (groups[g].files[f].action != -2) - groups[g].files[to++] = groups[g].files[f]; - - groups[g].filecount = to; - - /* reposition cursor, if necessary */ - if (cursorgroup == g && cursorfile > 0 && cursorfile >= groups[g].filecount) - cursorfile = groups[g].filecount - 1; - } - - if (loginfo != 0) - log_close(loginfo); - - if (deletedbytes < 1000.0) - format_status_left(status, L"Deleted %ld files (occupying %.0f bytes).", totaldeleted, deletedbytes); - else if (deletedbytes <= (1000.0 * 1000.0)) - format_status_left(status, L"Deleted %ld files (occupying %.1f KB).", totaldeleted, deletedbytes / 1000.0); - else if (deletedbytes <= (1000.0 * 1000.0 * 1000.0)) - format_status_left(status, L"Deleted %ld files (occupying %.1f MB).", totaldeleted, deletedbytes / (1000.0 * 1000.0)); - else - format_status_left(status, L"Deleted %ld files (occupying %.1f GB).", totaldeleted, deletedbytes / (1000.0 * 1000.0 * 1000.0)); - - /* delist empty groups */ - to = 0; - for (g = 0; g < totalgroups; ++g) - { - if (groups[g].filecount > 0) - { - groups[to] = groups[g]; - - /* reposition cursor, if necessary */ - if (to == cursorgroup && to != g) - cursorfile = 0; - - ++to; - } - else - { - free(groups[g].files); - } - } - - totalgroups = to; - - /* reposition cursor, if necessary */ - if (cursorgroup >= totalgroups) - { - cursorgroup = totalgroups - 1; - cursorfile = 0; - } - - /* recalculate line boundaries */ - adjusttopline = 1; - toplineoffset = 0; - groupfirstline = 0; - - for (g = 0; g < totalgroups; ++g) - { - if (adjusttopline && groups[g].endline >= topline) - toplineoffset = groups[g].endline - topline; - - groups[g].startline = groupfirstline; - groups[g].endline = groupfirstline + 2; - - for (f = 0; f < groups[g].filecount; ++f) - groups[g].endline += filerowcount(groups[g].files[f].file, COLS, groups[g].filecount); - - if (adjusttopline && toplineoffset > 0) - { - topline = groups[g].endline - toplineoffset; - - if (topline < 0) - topline = 0; - - adjusttopline = 0; - } - - groupfirstline = groups[g].endline + 1; - } - - if (totalgroups > 0 && groups[totalgroups-1].endline <= topline) - { - topline = groups[totalgroups-1].endline - getmaxy(filewin) + 1; - - if (topline < 0) - topline = 0; - } - + cmd_prune(groups, totalgroups, commandarguments, &globaldeletiontally, &totalgroups, &cursorgroup, &cursorfile, &topline, logfile, filewin, status); break; case KEY_RESIZE: |