diff -Nru fdupes-2.1.2/CHANGES fdupes-2.2.1/CHANGES --- fdupes-2.1.2/CHANGES 2020-08-12 18:04:27.000000000 +0000 +++ fdupes-2.2.1/CHANGES 2022-09-04 02:55:50.000000000 +0000 @@ -6,6 +6,14 @@ those who've otherwise worked on that item. For a list of contributors names and identifiers please see the CONTRIBUTORS file. +Changes from 2.1.2 to 2.2.0: + + - Add --deferconfirmation option. + - Check that files marked as duplicates haven't changed during program + execution before deleting them. + - Update documentation to indicate units for SIZE in command-line options. + - Move some configuration settings to configure.ac file. + Changes from 2.1.1 to 2.1.2: - Do not enter ncurses mode when --immediate option given. diff -Nru fdupes-2.1.2/commandidentifier.c fdupes-2.2.1/commandidentifier.c --- fdupes-2.1.2/commandidentifier.c 2020-08-12 18:04:27.000000000 +0000 +++ fdupes-2.2.1/commandidentifier.c 2022-09-04 02:55:50.000000000 +0000 @@ -1,4 +1,4 @@ -/* Copyright (c) 2018 Adrian Lopez +/* Copyright (c) 2018-2022 Adrian Lopez This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff -Nru fdupes-2.1.2/commandidentifier.h fdupes-2.2.1/commandidentifier.h --- fdupes-2.1.2/commandidentifier.h 2020-08-12 18:04:27.000000000 +0000 +++ fdupes-2.2.1/commandidentifier.h 2022-09-04 02:55:50.000000000 +0000 @@ -1,4 +1,4 @@ -/* Copyright (c) 2018 Adrian Lopez +/* Copyright (c) 2018-2022 Adrian Lopez This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff -Nru fdupes-2.1.2/configure.ac fdupes-2.2.1/configure.ac --- fdupes-2.1.2/configure.ac 2020-08-12 18:04:27.000000000 +0000 +++ fdupes-2.2.1/configure.ac 2022-09-04 02:55:50.000000000 +0000 @@ -1,4 +1,4 @@ -AC_INIT([fdupes], [2.1.2]) +AC_INIT([fdupes], [2.2.0]) AM_INIT_AUTOMAKE([foreign subdir-objects]) @@ -27,7 +27,7 @@ [AC_DEFINE([NO_NCURSES], [], [Do not compile against ncurses])] ) -AM_CONDITIONAL([NO_NCURSES], [test x"$with_ncurses" = x"no"]) +AM_CONDITIONAL([WITH_NCURSES], [test x"$with_ncurses" != x"no"]) unescaped_program_transform_name=`echo "${program_transform_name}"|sed -e "s&\\\\$\\\\$&\\\\$&g"` transformed_program_name=`echo "${PACKAGE_NAME}"|sed -e "${unescaped_program_transform_name}"|sed -e "s&\\\\\\\\&\\\\\\\\\\\\\\\\&g"` @@ -37,6 +37,10 @@ AC_DEFINE([_FILE_OFFSET_BITS], [64], [allow fdupes to handle files greater than (2<<31)-1 bytes]) +AC_DEFINE([CHUNK_SIZE], [8192], [number of bytes to read per read call]) +AC_DEFINE([PARTIAL_MD5_SIZE], [4096], [maximum number of bytes to use when calculating partial hashes]) +AC_DEFINE([INPUT_SIZE], [256], [size of command buffer (plain interactive mode only)]) + AC_CONFIG_FILES([Makefile]) AC_PROG_CC AC_OUTPUT diff -Nru fdupes-2.1.2/confirmmatch.c fdupes-2.2.1/confirmmatch.c --- fdupes-2.1.2/confirmmatch.c 1970-01-01 00:00:00.000000000 +0000 +++ fdupes-2.2.1/confirmmatch.c 2022-09-04 02:55:50.000000000 +0000 @@ -0,0 +1,48 @@ +/* FDUPES Copyright (c) 1999-2022 Adrian Lopez + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#include "config.h" +#include "confirmmatch.h" +#include + +/* Do a bit-for-bit comparison in case two different files produce the + same signature. Unlikely, but better safe than sorry. */ + +int confirmmatch(FILE *file1, FILE *file2) +{ + unsigned char c1[CHUNK_SIZE]; + unsigned char c2[CHUNK_SIZE]; + size_t r1; + size_t r2; + + fseek(file1, 0, SEEK_SET); + fseek(file2, 0, SEEK_SET); + + do { + r1 = fread(c1, sizeof(unsigned char), sizeof(c1), file1); + r2 = fread(c2, sizeof(unsigned char), sizeof(c2), file2); + + if (r1 != r2) return 0; /* file lengths are different */ + if (memcmp (c1, c2, r1)) return 0; /* file contents are different */ + } while (r2); + + return 1; +} diff -Nru fdupes-2.1.2/confirmmatch.h fdupes-2.2.1/confirmmatch.h --- fdupes-2.1.2/confirmmatch.h 1970-01-01 00:00:00.000000000 +0000 +++ fdupes-2.2.1/confirmmatch.h 2022-09-04 02:55:50.000000000 +0000 @@ -0,0 +1,29 @@ +/* FDUPES Copyright (c) 1999-2022 Adrian Lopez + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#ifndef CONFIRMMATCH_H +#define CONFIRMMATCH_H + +#include + +int confirmmatch(FILE *file1, FILE *file2); + +#endif \ No newline at end of file diff -Nru fdupes-2.1.2/debian/changelog fdupes-2.2.1/debian/changelog --- fdupes-2.1.2/debian/changelog 2021-02-25 13:59:31.000000000 +0000 +++ fdupes-2.2.1/debian/changelog 2022-09-28 03:35:24.000000000 +0000 @@ -1,8 +1,11 @@ -fdupes (1:2.1.2-1build1) hirsute; urgency=medium +fdupes (1:2.2.1-1) unstable; urgency=medium - * No change rebuild fixed ownership on ppc64el. + * New upstream release + * debian/copyright + - update upstream copyright notice + - extend packaging copyright years - -- Dimitri John Ledkov Thu, 25 Feb 2021 13:59:31 +0000 + -- Sandro Tosi Tue, 27 Sep 2022 23:35:24 -0400 fdupes (1:2.1.2-1) unstable; urgency=low diff -Nru fdupes-2.1.2/debian/control fdupes-2.2.1/debian/control --- fdupes-2.1.2/debian/control 2021-02-25 13:59:31.000000000 +0000 +++ fdupes-2.2.1/debian/control 2022-09-28 03:35:24.000000000 +0000 @@ -1,8 +1,7 @@ Source: fdupes Section: utils Priority: optional -Maintainer: Ubuntu Developers -XSBC-Original-Maintainer: Sandro Tosi +Maintainer: Sandro Tosi Build-Depends: debhelper (>= 11), debhelper-compat (= 13), libncurses-dev, diff -Nru fdupes-2.1.2/debian/copyright fdupes-2.2.1/debian/copyright --- fdupes-2.1.2/debian/copyright 2021-01-14 05:21:41.000000000 +0000 +++ fdupes-2.2.1/debian/copyright 2022-09-28 03:35:24.000000000 +0000 @@ -3,12 +3,12 @@ Source: https://github.com/adrianlopezroche/fdupes Files: * -Copyright: Copyright (c) 1999-2019 Adrian Lopez +Copyright: Copyright (c) 1999-2022 Adrian Lopez License: MIT Files: debian/* Copyright: Copyright (C) 2000-2003 Adrian Bridgett - Copyright (C) 2008-2021, Sandro Tosi + Copyright (C) 2008-2022, Sandro Tosi License: MIT License: MIT diff -Nru fdupes-2.1.2/dir.c fdupes-2.2.1/dir.c --- fdupes-2.1.2/dir.c 2020-08-12 18:04:27.000000000 +0000 +++ fdupes-2.2.1/dir.c 2022-09-04 02:55:50.000000000 +0000 @@ -1,4 +1,4 @@ -/* Copyright (c) 2018 Adrian Lopez +/* Copyright (c) 2018-2022 Adrian Lopez This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff -Nru fdupes-2.1.2/dir.h fdupes-2.2.1/dir.h --- fdupes-2.1.2/dir.h 2020-08-12 18:04:27.000000000 +0000 +++ fdupes-2.2.1/dir.h 2022-09-04 02:55:50.000000000 +0000 @@ -1,4 +1,4 @@ -/* Copyright (c) 2018 Adrian Lopez +/* Copyright (c) 2018-2022 Adrian Lopez This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff -Nru fdupes-2.1.2/errormsg.c fdupes-2.2.1/errormsg.c --- fdupes-2.1.2/errormsg.c 2020-08-12 18:04:27.000000000 +0000 +++ fdupes-2.2.1/errormsg.c 2022-09-04 02:55:50.000000000 +0000 @@ -1,4 +1,4 @@ -/* Copyright (c) 2018 Adrian Lopez +/* Copyright (c) 2018-2022 Adrian Lopez This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff -Nru fdupes-2.1.2/errormsg.h fdupes-2.2.1/errormsg.h --- fdupes-2.1.2/errormsg.h 2020-08-12 18:04:27.000000000 +0000 +++ fdupes-2.2.1/errormsg.h 2022-09-04 02:55:50.000000000 +0000 @@ -1,4 +1,4 @@ -/* Copyright (c) 2018 Adrian Lopez +/* Copyright (c) 2018-2022 Adrian Lopez This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff -Nru fdupes-2.1.2/fdupes.1 fdupes-2.2.1/fdupes.1 --- fdupes-2.1.2/fdupes.1 2020-08-12 18:04:27.000000000 +0000 +++ fdupes-2.2.1/fdupes.1 2022-09-04 02:55:50.000000000 +0000 @@ -34,10 +34,10 @@ treated as non-duplicates; this option will change this behavior .TP .B -G --minsize\fR=\fISIZE\fR -consider only files greater than or equal to SIZE +consider only files greater than or equal to SIZE in bytes .TP .B -L --maxsize=\fR=\fISIZE\fR -consider only files less than or equal to SIZE +consider only files less than or equal to SIZE in bytes .TP .B -n --noempty exclude zero-length files from consideration @@ -68,6 +68,10 @@ .B CAVEATS below) .TP +.B -D --deferconfirmation +in interactive mode, defer byte-for-byte confirmation of +duplicates until just before file deletion +.TP .B -P --plain with --delete, use line-based prompt (as with older versions of fdupes) instead of screen-mode interface diff -Nru fdupes-2.1.2/fdupes.c fdupes-2.2.1/fdupes.c --- fdupes-2.1.2/fdupes.c 2020-08-12 18:04:27.000000000 +0000 +++ fdupes-2.2.1/fdupes.c 2022-09-04 02:55:50.000000000 +0000 @@ -1,4 +1,4 @@ -/* FDUPES Copyright (c) 1999-2018 Adrian Lopez +/* FDUPES Copyright (c) 1999-2022 Adrian Lopez Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files @@ -44,10 +44,12 @@ #include "ncurses-interface.h" #endif #include "fdupes.h" +#include "confirmmatch.h" #include "errormsg.h" #include "log.h" #include "sigint.h" #include "flags.h" +#include "removeifnotchanged.h" long long minsize = -1; long long maxsize = -1; @@ -62,32 +64,8 @@ ordertype_t ordertype = ORDER_MTIME; -#define CHUNK_SIZE 8192 - -#define INPUT_SIZE 256 - -#define PARTIAL_MD5_SIZE 4096 - #define MD5_DIGEST_LENGTH 16 -/* - -TODO: Partial sums (for working with very large files). - -typedef struct _signature -{ - md5_state_t state; - md5_byte_t digest[16]; -} signature_t; - -typedef struct _signatures -{ - int num_signatures; - signature_t *signatures; -} signatures_t; - -*/ - typedef struct _filetree { file_t *file; struct _filetree *left; @@ -304,7 +282,7 @@ } /* ignore logfile */ - if (info.st_dev == logfile_status->st_dev && info.st_ino == logfile_status->st_ino) + if (logfile_status != 0 && info.st_dev == logfile_status->st_dev && info.st_ino == logfile_status->st_ino) { free(newfile->d_name); free(newfile); @@ -628,7 +606,6 @@ } cmpresult = md5cmp(file->crcpartial, checktree->file->crcpartial); - /*if (cmpresult != 0) errormsg(" on %s vs %s\n", file->d_name, checktree->file->d_name);*/ if (cmpresult == 0) { if (checktree->file->crcsignature == NULL) { @@ -656,11 +633,6 @@ } cmpresult = md5cmp(file->crcsignature, checktree->file->crcsignature); - /*if (cmpresult != 0) errormsg("P on %s vs %s\n", - file->d_name, checktree->file->d_name); - else errormsg("P F on %s vs %s\n", file->d_name, - checktree->file->d_name); - printf("%s matches %s\n", file->d_name, checktree->file->d_name);*/ } } @@ -684,30 +656,6 @@ } } -/* Do a bit-for-bit comparison in case two different files produce the - same signature. Unlikely, but better safe than sorry. */ - -int confirmmatch(FILE *file1, FILE *file2) -{ - unsigned char c1[CHUNK_SIZE]; - unsigned char c2[CHUNK_SIZE]; - size_t r1; - size_t r2; - - fseek(file1, 0, SEEK_SET); - fseek(file2, 0, SEEK_SET); - - do { - r1 = fread(c1, sizeof(unsigned char), sizeof(c1), file1); - r2 = fread(c2, sizeof(unsigned char), sizeof(c2), file2); - - if (r1 != r2) return 0; /* file lengths are different */ - if (memcmp (c1, c2, r1)) return 0; /* file contents are different */ - } while (r2); - - return 1; -} - void summarizematches(file_t *files) { int numsets = 0; @@ -843,6 +791,7 @@ file_t *curfile; file_t **dupelist; int *preserve; + int firstpreserved; char *preservestr; char *token; char *tstr; @@ -853,6 +802,10 @@ int i; struct log_info *loginfo; int log_error; + FILE *file1; + FILE *file2; + int ismatch; + char *errorstring; curfile = files; @@ -1022,18 +975,59 @@ log_file_remaining(loginfo, dupelist[x]->d_name); } else { - if (remove(dupelist[x]->d_name) == 0) { - printf(" [-] %s\n", dupelist[x]->d_name); + if (ISFLAG(flags, F_DEFERCONFIRMATION)) + { + firstpreserved = 0; + for (i = 1; i <= counter; ++i) + { + if (preserve[i]) + { + firstpreserved = i; + break; + } + } - if (loginfo) - log_file_deleted(loginfo, dupelist[x]->d_name); - } else { - printf(" [!] %s ", dupelist[x]->d_name); - printf("-- unable to delete file!\n"); + file1 = fopen(dupelist[x]->d_name, "rb"); + file2 = fopen(dupelist[firstpreserved]->d_name, "rb"); - if (loginfo) - log_file_remaining(loginfo, dupelist[x]->d_name); - } + if (file1 && file2) + ismatch = confirmmatch(file1, file2); + else + ismatch = 0; + + if (file2) + fclose(file2); + + if (file1) + fclose(file1); + } + else + { + ismatch = 1; + } + + if (ismatch) { + if (removeifnotchanged(dupelist[x], &errorstring) == 0) { + printf(" [-] %s\n", dupelist[x]->d_name); + + if (loginfo) + log_file_deleted(loginfo, dupelist[x]->d_name); + } + else { + printf(" [!] %s ", dupelist[x]->d_name); + printf("-- unable to delete file: %s!\n", errorstring); + + if (loginfo) + log_file_remaining(loginfo, dupelist[x]->d_name); + } + } + else { + printf(" [!] %s\n", dupelist[x]->d_name); + printf(" -- unable to confirm match; file not deleted!\n"); + + if (loginfo) + log_file_remaining(loginfo, dupelist[x]->d_name); + } } } printf("\n"); @@ -1133,11 +1127,12 @@ } } -void deletesuccessor(file_t **existing, file_t *duplicate, +void deletesuccessor(file_t **existing, file_t *duplicate, int matchconfirmed, int (*comparef)(file_t *f1, file_t *f2), struct log_info *loginfo) { file_t *to_keep; file_t *to_delete; + char *errorstring; if (comparef(duplicate, *existing) >= 0) { @@ -1162,14 +1157,25 @@ if (loginfo) log_file_remaining(loginfo, to_keep->d_name); - if (remove(to_delete->d_name) == 0) { - printf(" [-] %s\n", to_delete->d_name); + if (matchconfirmed) + { + if (removeifnotchanged(to_delete, &errorstring) == 0) { + printf(" [-] %s\n", to_delete->d_name); - if (loginfo) - log_file_deleted(loginfo, to_delete->d_name); - } else { - printf(" [!] %s ", to_delete->d_name); - printf("-- unable to delete file!\n"); + if (loginfo) + log_file_deleted(loginfo, to_delete->d_name); + } else { + printf(" [!] %s ", to_delete->d_name); + printf("-- unable to delete file: %s!\n", errorstring); + + if (loginfo) + log_file_remaining(loginfo, to_delete->d_name); + } + } + else + { + printf(" [!] %s\n", to_delete->d_name); + printf(" -- unable to confirm match; file not deleted!\n"); if (loginfo) log_file_remaining(loginfo, to_delete->d_name); @@ -1213,6 +1219,8 @@ printf(" with -s or --symlinks, or when specifying a\n"); printf(" particular directory more than once; refer to the\n"); printf(" fdupes documentation for additional information\n"); + printf(" -D --deferconfirmation in interactive mode, defer byte-for-byte confirmation\n"); + printf(" of duplicates until just before file deletion\n"); #ifndef NO_NCURSES printf(" -P --plain with --delete, use line-based prompt (as with older\n"); printf(" versions of fdupes) instead of screen-mode interface\n"); @@ -1283,6 +1291,7 @@ { "order", 1, 0, 'o' }, { "reverse", 0, 0, 'i' }, { "log", 1, 0, 'l' }, + { "deferconfirmation", 0, 0, 'D' }, { 0, 0, 0, 0 } }; #define GETOPT getopt_long @@ -1296,7 +1305,7 @@ oldargv = cloneargs(argc, argv); - while ((opt = GETOPT(argc, argv, "frRq1StsHG:L:nAdPvhNImpo:il:" + while ((opt = GETOPT(argc, argv, "frRq1StsHG:L:nAdPvhNImpo:il:D" #ifdef HAVE_GETOPT_H , long_options, NULL #endif @@ -1393,6 +1402,9 @@ case 'l': logfile = optarg; break; + case 'D': + SETFLAG(flags, F_DEFERCONFIRMATION); + break; default: fprintf(stderr, "Try `fdupes --help' for more information.\n"); exit(1); @@ -1414,6 +1426,12 @@ exit(1); } + if (ISFLAG(flags, F_DEFERCONFIRMATION) && (!ISFLAG(flags, F_DELETEFILES) || ISFLAG(flags, F_NOPROMPT))) + { + errormsg("--deferconfirmation only works with interactive deletion modes\n"); + exit(1); + } + if (!ISFLAG(flags, F_DELETEFILES)) logfile = 0; @@ -1450,16 +1468,16 @@ /* F_RECURSE is not set for directories before --recurse: */ for (x = optind; x < firstrecurse; x++) - filecount += grokdir(argv[x], &files, &logfile_status); + filecount += grokdir(argv[x], &files, logfile ? &logfile_status : 0); /* Set F_RECURSE for directories after --recurse: */ SETFLAG(flags, F_RECURSE); for (x = firstrecurse; x < argc; x++) - filecount += grokdir(argv[x], &files, &logfile_status); + filecount += grokdir(argv[x], &files, logfile ? &logfile_status : 0); } else { for (x = optind; x < argc; x++) - filecount += grokdir(argv[x], &files, &logfile_status); + filecount += grokdir(argv[x], &files, logfile ? &logfile_status : 0); } if (!files) { @@ -1489,19 +1507,19 @@ continue; } - if (confirmmatch(file1, file2)) { - if (ISFLAG(flags, F_DELETEFILES) && ISFLAG(flags, F_IMMEDIATE)) - deletesuccessor(match, curfile, + if (ISFLAG(flags, F_DELETEFILES) && ISFLAG(flags, F_IMMEDIATE)) + { + deletesuccessor(match, curfile, confirmmatch(file1, file2), ordertype == ORDER_MTIME ? sort_pairs_by_mtime : ordertype == ORDER_CTIME ? sort_pairs_by_ctime : sort_pairs_by_filename, loginfo ); - else - registerpair(match, curfile, - ordertype == ORDER_MTIME ? sort_pairs_by_mtime : - ordertype == ORDER_CTIME ? sort_pairs_by_ctime : - sort_pairs_by_filename ); } - + else if (ISFLAG(flags, F_DEFERCONFIRMATION) || confirmmatch(file1, file2)) + registerpair(match, curfile, + ordertype == ORDER_MTIME ? sort_pairs_by_mtime : + ordertype == ORDER_CTIME ? sort_pairs_by_ctime : + sort_pairs_by_filename ); + fclose(file1); fclose(file2); } diff -Nru fdupes-2.1.2/fdupes.h fdupes-2.2.1/fdupes.h --- fdupes-2.1.2/fdupes.h 2020-08-12 18:04:27.000000000 +0000 +++ fdupes-2.2.1/fdupes.h 2022-09-04 02:55:50.000000000 +0000 @@ -1,4 +1,4 @@ -/* FDUPES Copyright (c) 2018 Adrian Lopez +/* FDUPES Copyright (c) 2018-2022 Adrian Lopez Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files diff -Nru fdupes-2.1.2/fileaction.c fdupes-2.2.1/fileaction.c --- fdupes-2.1.2/fileaction.c 1970-01-01 00:00:00.000000000 +0000 +++ fdupes-2.2.1/fileaction.c 2022-09-04 02:55:50.000000000 +0000 @@ -0,0 +1,39 @@ +/* FDUPES Copyright (c) 1999-2022 Adrian Lopez + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#include "fileaction.h" + +char getfileactionchar(int action) +{ + switch (action) + { + case FILEACTION_UNRESOLVED: + return ' '; + case FILEACTION_KEEP: + return '+'; + case FILEACTION_DELETE: + return '-'; + case FILEACTION_ERROR: + return '!'; + default: + return '?'; + } +} \ No newline at end of file diff -Nru fdupes-2.1.2/fileaction.h fdupes-2.2.1/fileaction.h --- fdupes-2.1.2/fileaction.h 1970-01-01 00:00:00.000000000 +0000 +++ fdupes-2.2.1/fileaction.h 2022-09-04 02:55:50.000000000 +0000 @@ -0,0 +1,33 @@ +/* FDUPES Copyright (c) 1999-2022 Adrian Lopez + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#ifndef FILEACTION_H +#define FILEACTION_H + +#define FILEACTION_UNRESOLVED 0 +#define FILEACTION_KEEP 1 +#define FILEACTION_DELETE -1 +#define FILEACTION_DELIST -2 +#define FILEACTION_ERROR -3 + +char getfileactionchar(int action); + +#endif \ No newline at end of file diff -Nru fdupes-2.1.2/filegroup.h fdupes-2.2.1/filegroup.h --- fdupes-2.1.2/filegroup.h 2020-08-12 18:04:27.000000000 +0000 +++ fdupes-2.2.1/filegroup.h 2022-09-04 02:55:50.000000000 +0000 @@ -1,4 +1,4 @@ -/* FDUPES Copyright (c) 2018 Adrian Lopez +/* FDUPES Copyright (c) 2018-2022 Adrian Lopez Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files diff -Nru fdupes-2.1.2/flags.h fdupes-2.2.1/flags.h --- fdupes-2.1.2/flags.h 2020-08-12 18:04:27.000000000 +0000 +++ fdupes-2.2.1/flags.h 2022-09-04 02:55:50.000000000 +0000 @@ -22,7 +22,8 @@ #define F_IMMEDIATE 0x8000 #define F_PLAINPROMPT 0x10000 #define F_SHOWTIME 0x20000 +#define F_DEFERCONFIRMATION 0x40000 extern unsigned long flags; -#endif \ No newline at end of file +#endif diff -Nru fdupes-2.1.2/fmatch.c fdupes-2.2.1/fmatch.c --- fdupes-2.1.2/fmatch.c 2020-08-12 18:04:27.000000000 +0000 +++ fdupes-2.2.1/fmatch.c 2022-09-04 02:55:50.000000000 +0000 @@ -1,4 +1,4 @@ -/* Copyright (c) 2018 Adrian Lopez +/* Copyright (c) 2018-2022 Adrian Lopez This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff -Nru fdupes-2.1.2/fmatch.h fdupes-2.2.1/fmatch.h --- fdupes-2.1.2/fmatch.h 2020-08-12 18:04:27.000000000 +0000 +++ fdupes-2.2.1/fmatch.h 2022-09-04 02:55:50.000000000 +0000 @@ -1,4 +1,4 @@ -/* Copyright (c) 2018 Adrian Lopez +/* Copyright (c) 2018-2022 Adrian Lopez This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff -Nru fdupes-2.1.2/log.c fdupes-2.2.1/log.c --- fdupes-2.1.2/log.c 2020-08-12 18:04:27.000000000 +0000 +++ fdupes-2.2.1/log.c 2022-09-04 02:55:50.000000000 +0000 @@ -1,4 +1,4 @@ -/* Copyright (c) 2018 Adrian Lopez +/* Copyright (c) 2018-2022 Adrian Lopez This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff -Nru fdupes-2.1.2/log.h fdupes-2.2.1/log.h --- fdupes-2.1.2/log.h 2020-08-12 18:04:27.000000000 +0000 +++ fdupes-2.2.1/log.h 2022-09-04 02:55:50.000000000 +0000 @@ -1,4 +1,4 @@ -/* Copyright (c) 2018 Adrian Lopez +/* Copyright (c) 2018-2022 Adrian Lopez This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff -Nru fdupes-2.1.2/Makefile.am fdupes-2.2.1/Makefile.am --- fdupes-2.1.2/Makefile.am 2020-08-12 18:04:27.000000000 +0000 +++ fdupes-2.2.1/Makefile.am 2022-09-04 02:55:50.000000000 +0000 @@ -1,6 +1,5 @@ bin_PROGRAMS = fdupes -if NO_NCURSES fdupes_SOURCES = fdupes.c\ fdupes.h\ errormsg.c\ @@ -15,16 +14,20 @@ sigint.h\ flags.c\ flags.h\ + confirmmatch.c\ + confirmmatch.h\ + removeifnotchanged.c\ + removeifnotchanged.h\ mbstowcs_escape_invalid.c\ mbstowcs_escape_invalid.h\ md5/md5.c\ md5/md5.h dist_man1_MANS = fdupes.1 -else -fdupes_SOURCES = fdupes.c\ - fdupes.h\ - filegroup.h\ +if WITH_NCURSES +fdupes_SOURCES += filegroup.h\ + fileaction.h\ + fileaction.c\ ncurses-commands.c\ ncurses-commands.h\ ncurses-getcommand.c\ @@ -39,27 +42,10 @@ ncurses-status.h\ commandidentifier.c\ commandidentifier.h\ - errormsg.c\ - errormsg.h\ wcs.c\ wcs.h\ - dir.c\ - dir.h\ - log.c\ - log.h\ - fmatch.c\ - fmatch.h\ - sigint.c\ - sigint.h\ - flags.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 + positive_wcwidth.h dist_man7_MANS = fdupes-help.7 endif diff -Nru fdupes-2.1.2/mbstowcs_escape_invalid.c fdupes-2.2.1/mbstowcs_escape_invalid.c --- fdupes-2.1.2/mbstowcs_escape_invalid.c 2020-08-12 18:04:27.000000000 +0000 +++ fdupes-2.2.1/mbstowcs_escape_invalid.c 2022-09-04 02:55:50.000000000 +0000 @@ -1,4 +1,4 @@ -/* Copyright (c) 2019 Adrian Lopez +/* Copyright (c) 2019-2022 Adrian Lopez This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff -Nru fdupes-2.1.2/mbstowcs_escape_invalid.h fdupes-2.2.1/mbstowcs_escape_invalid.h --- fdupes-2.1.2/mbstowcs_escape_invalid.h 2020-08-12 18:04:27.000000000 +0000 +++ fdupes-2.2.1/mbstowcs_escape_invalid.h 2022-09-04 02:55:50.000000000 +0000 @@ -1,4 +1,4 @@ -/* Copyright (c) 2019 Adrian Lopez +/* Copyright (c) 2019-2022 Adrian Lopez This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff -Nru fdupes-2.1.2/ncurses-commands.c fdupes-2.2.1/ncurses-commands.c --- fdupes-2.1.2/ncurses-commands.c 2020-08-12 18:04:27.000000000 +0000 +++ fdupes-2.2.1/ncurses-commands.c 2022-09-04 02:55:50.000000000 +0000 @@ -1,4 +1,4 @@ -/* FDUPES Copyright (c) 2018 Adrian Lopez +/* FDUPES Copyright (c) 2018-2022 Adrian Lopez Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files @@ -22,9 +22,14 @@ #include "config.h" #include "ncurses-status.h" #include "ncurses-commands.h" +#include "fileaction.h" +#include "flags.h" +#include "confirmmatch.h" +#include "errormsg.h" #include "wcs.h" #include "mbstowcs_escape_invalid.h" #include "log.h" +#include "removeifnotchanged.h" #include #include @@ -619,7 +624,7 @@ { if (groups[g].files[f].selected) { - set_file_action(&groups[g].files[f], 1, deletiontally); + set_file_action(&groups[g].files[f], FILEACTION_KEEP, deletiontally); ++keepfilecount; } } @@ -643,7 +648,7 @@ { if (groups[g].files[f].selected) { - set_file_action(&groups[g].files[f], -1, deletiontally); + set_file_action(&groups[g].files[f], FILEACTION_DELETE, deletiontally); ++deletefilecount; } } @@ -667,7 +672,7 @@ { if (groups[g].files[f].selected) { - set_file_action(&groups[g].files[f], 0, deletiontally); + set_file_action(&groups[g].files[f], FILEACTION_UNRESOLVED, deletiontally); ++resetfilecount; } } @@ -681,12 +686,13 @@ 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 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, WINDOW *statuswin, struct status_text *status) { int deletecount; int preservecount; int unresolvedcount; int totaldeleted = 0; + int totalfailed = 0; double deletedbytes = 0; struct log_info *loginfo; int g; @@ -695,6 +701,11 @@ int adjusttopline; int toplineoffset; int groupfirstline; + FILE *file1; + FILE *file2; + int ismatch; + wchar_t *statuscopy; + struct groupfile *firstnotdeleted; if (logfile != 0) loginfo = log_open(logfile, 0); @@ -706,19 +717,31 @@ preservecount = 0; deletecount = 0; unresolvedcount = 0; + firstnotdeleted = 0; for (f = 0; f < groups[g].filecount; ++f) { switch (groups[g].files[f].action) { - case -1: + case FILEACTION_DELETE: ++deletecount; break; - case 0: + + case FILEACTION_UNRESOLVED: + case FILEACTION_ERROR: ++unresolvedcount; + + if (firstnotdeleted == 0) + firstnotdeleted = &groups[g].files[f]; + break; - case 1: + + case FILEACTION_KEEP: ++preservecount; + + if (firstnotdeleted == 0) + firstnotdeleted = &groups[g].files[f]; + break; } } @@ -731,11 +754,36 @@ { for (f = 0; f < groups[g].filecount; ++f) { - if (groups[g].files[f].action == -1) + if (groups[g].files[f].action == FILEACTION_DELETE) { - if (remove(groups[g].files[f].file->d_name) == 0) + if (ISFLAG(flags, F_DEFERCONFIRMATION)) { - set_file_action(&groups[g].files[f], -2, deletiontally); + format_status_left(status, L"Confirming duplicates..."); + print_status(statuswin, status); + wrefresh(statuswin); + + file1 = fopen(groups[g].files[f].file->d_name, "rb"); + file2 = fopen(firstnotdeleted->file->d_name, "rb"); + + if (file1 && file2) + ismatch = confirmmatch(file1, file2); + else + ismatch = 0; + + if (file2) + fclose(file2); + + if (file1) + fclose(file1); + } + else + { + ismatch = 1; + } + + if (ismatch && removeifnotchanged(groups[g].files[f].file, 0) == 0) + { + set_file_action(&groups[g].files[f], FILEACTION_DELIST, deletiontally); deletedbytes += groups[g].files[f].file->size; ++totaldeleted; @@ -743,6 +791,12 @@ if (loginfo) log_file_deleted(loginfo, groups[g].files[f].file->d_name); } + else + { + set_file_action(&groups[g].files[f], FILEACTION_ERROR, deletiontally); + unresolvedcount++; + totalfailed++; + } } } @@ -750,7 +804,8 @@ { for (f = 0; f < groups[g].filecount; ++f) { - if (groups[g].files[f].action >= 0) + if (groups[g].files[f].action != FILEACTION_DELETE && + groups[g].files[f].action != FILEACTION_DELIST) log_file_remaining(loginfo, groups[g].files[f].file->d_name); } } @@ -765,8 +820,8 @@ 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); + if (groups[g].files[f].action == FILEACTION_KEEP) + set_file_action(&groups[g].files[f], FILEACTION_DELIST, deletiontally); preservecount = 0; } @@ -774,14 +829,14 @@ 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); + if (groups[g].files[f].action == FILEACTION_UNRESOLVED || groups[g].files[f].action == FILEACTION_ERROR) + set_file_action(&groups[g].files[f], FILEACTION_DELIST, deletiontally); } /* delist any files marked for delisting */ to = 0; for (f = 0; f < groups[g].filecount; ++f) - if (groups[g].files[f].action != -2) + if (groups[g].files[f].action != FILEACTION_DELIST) groups[g].files[to++] = groups[g].files[f]; groups[g].filecount = to; @@ -795,13 +850,30 @@ log_close(loginfo); if (deletedbytes < 1000.0) - format_status_left(status, L"Deleted %ld files (occupying %.0f bytes).", totaldeleted, deletedbytes); + format_status_left(status, L"Deleted %ld files (occupying %.0f bytes)%c", totaldeleted, deletedbytes, totalfailed ? ';' : '.'); else if (deletedbytes <= (1000.0 * 1000.0)) - format_status_left(status, L"Deleted %ld files (occupying %.1f KB).", totaldeleted, deletedbytes / 1000.0); + format_status_left(status, L"Deleted %ld files (occupying %.1f KB)%c", totaldeleted, deletedbytes / 1000.0, totalfailed ? ';' : '.'); 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)); + format_status_left(status, L"Deleted %ld files (occupying %.1f MB)%c", totaldeleted, deletedbytes / (1000.0 * 1000.0), totalfailed ? ';' : '.'); else - format_status_left(status, L"Deleted %ld files (occupying %.1f GB).", totaldeleted, deletedbytes / (1000.0 * 1000.0 * 1000.0)); + format_status_left(status, L"Deleted %ld files (occupying %.1f GB)%c", totaldeleted, deletedbytes / (1000.0 * 1000.0 * 1000.0), totalfailed ? ';' : '.'); + + if (totalfailed > 0) + { + statuscopy = malloc(sizeof(wchar_t) * (wcslen(status->left) + 1)); + if (!statuscopy) + { + endwin(); + errormsg("out of memory\n"); + exit(1); + } + + wcsncpy(statuscopy, status->left, wcslen(status->left) + 1); + + format_status_left(status, L"%S %d failed.", statuscopy, totalfailed); + + free(statuscopy); + } /* delist empty groups */ to = 0; diff -Nru fdupes-2.1.2/ncurses-commands.h fdupes-2.2.1/ncurses-commands.h --- fdupes-2.1.2/ncurses-commands.h 2020-08-12 18:04:27.000000000 +0000 +++ fdupes-2.2.1/ncurses-commands.h 2022-09-04 02:55:50.000000000 +0000 @@ -1,4 +1,4 @@ -/* FDUPES Copyright (c) 2018 Adrian Lopez +/* FDUPES Copyright (c) 2018-2022 Adrian Lopez Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files @@ -71,6 +71,6 @@ 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);; +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, WINDOW *statuswin, struct status_text *status);; #endif \ No newline at end of file diff -Nru fdupes-2.1.2/ncurses-getcommand.c fdupes-2.2.1/ncurses-getcommand.c --- fdupes-2.1.2/ncurses-getcommand.c 2020-08-12 18:04:27.000000000 +0000 +++ fdupes-2.2.1/ncurses-getcommand.c 2022-09-04 02:55:50.000000000 +0000 @@ -1,4 +1,4 @@ -/* FDUPES Copyright (c) 2018 Adrian Lopez +/* FDUPES Copyright (c) 2018-2022 Adrian Lopez Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files diff -Nru fdupes-2.1.2/ncurses-getcommand.h fdupes-2.2.1/ncurses-getcommand.h --- fdupes-2.1.2/ncurses-getcommand.h 2020-08-12 18:04:27.000000000 +0000 +++ fdupes-2.2.1/ncurses-getcommand.h 2022-09-04 02:55:50.000000000 +0000 @@ -1,4 +1,4 @@ -/* FDUPES Copyright (c) 2018 Adrian Lopez +/* FDUPES Copyright (c) 2018-2022 Adrian Lopez Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files diff -Nru fdupes-2.1.2/ncurses-interface.c fdupes-2.2.1/ncurses-interface.c --- fdupes-2.1.2/ncurses-interface.c 2020-08-12 18:04:27.000000000 +0000 +++ fdupes-2.2.1/ncurses-interface.c 2022-09-04 02:55:50.000000000 +0000 @@ -1,4 +1,4 @@ -/* FDUPES Copyright (c) 2018 Adrian Lopez +/* FDUPES Copyright (c) 2018-2022 Adrian Lopez Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files @@ -34,6 +34,7 @@ #include "ncurses-prompt.h" #include "ncurses-status.h" #include "ncurses-print.h" +#include "fileaction.h" #include "mbstowcs_escape_invalid.h" #include "positive_wcwidth.h" #include "commandidentifier.h" @@ -182,13 +183,13 @@ { switch (file->action) { - case -1: - if (new_action != -1) + case FILEACTION_DELETE: + if (new_action != FILEACTION_DELETE) --*deletion_tally; break; default: - if (new_action == -1) + if (new_action == FILEACTION_DELETE) ++*deletion_tally; break; } @@ -539,7 +540,7 @@ do { groups[totalgroups].files[groupfilecount].file = dupefile; - groups[totalgroups].files[groupfilecount].action = 0; + groups[totalgroups].files[groupfilecount].action = FILEACTION_UNRESOLVED; groups[totalgroups].files[groupfilecount].selected = 0; ++groupfilecount; @@ -647,7 +648,7 @@ { print_spaces(filewin, index_width); - wprintw(filewin, " [%c] ", groups[groupindex].files[f].action > 0 ? '+' : groups[groupindex].files[f].action < 0 ? '-' : ' '); + wprintw(filewin, " [%c] ", getfileactionchar(groups[groupindex].files[f].action)); if (ISFLAG(flags, F_SHOWTIME)) wprintw(filewin, "[%s] ", fmttime(groups[groupindex].files[f].file->mtime)); @@ -673,7 +674,7 @@ if (cursorgroup == groupindex && cursorfile == f) wattron(filewin, A_REVERSE); - wprintw(filewin, "[%c]", groups[groupindex].files[f].action > 0 ? '+' : groups[groupindex].files[f].action < 0 ? '-' : ' '); + wprintw(filewin, "[%c]", getfileactionchar(groups[groupindex].files[f].action)); if (cursorgroup == groupindex && cursorfile == f) wattroff(filewin, A_REVERSE); wprintw(filewin, " "); @@ -829,7 +830,7 @@ case COMMAND_RESET_GROUP: for (x = 0; x < groups[cursorgroup].filecount; ++x) - set_file_action(&groups[cursorgroup].files[x], 0, &globaldeletiontally); + set_file_action(&groups[cursorgroup].files[x], FILEACTION_UNRESOLVED, &globaldeletiontally); format_status_left(status, L"Reset all files in current group."); @@ -838,7 +839,7 @@ case COMMAND_PRESERVE_ALL: /* mark all files for preservation */ for (x = 0; x < groups[cursorgroup].filecount; ++x) - set_file_action(&groups[cursorgroup].files[x], 1, &globaldeletiontally); + set_file_action(&groups[cursorgroup].files[x], FILEACTION_KEEP, &globaldeletiontally); format_status_left(status, L"%d files marked for preservation", groups[cursorgroup].filecount); @@ -881,7 +882,7 @@ break; case COMMAND_PRUNE: - cmd_prune(groups, totalgroups, commandarguments, &globaldeletiontally, &totalgroups, &cursorgroup, &cursorfile, &topline, logfile, filewin, status); + cmd_prune(groups, totalgroups, commandarguments, &globaldeletiontally, &totalgroups, &cursorgroup, &cursorfile, &topline, logfile, filewin, statuswin, status); break; case COMMAND_EXIT: /* exit program */ @@ -1016,7 +1017,7 @@ if (wcstolcheck != token && *wcstolcheck == '\0') { if (number > 0 && number <= groups[cursorgroup].filecount) - set_file_action(&groups[cursorgroup].files[number - 1], 1, &globaldeletiontally); + set_file_action(&groups[cursorgroup].files[number - 1], FILEACTION_KEEP, &globaldeletiontally); } token = wcstok(NULL, L",", &wcsptr); @@ -1028,9 +1029,9 @@ for (x = 0; x < groups[cursorgroup].filecount; ++x) { - if (groups[cursorgroup].files[x].action == 1) + if (groups[cursorgroup].files[x].action == FILEACTION_KEEP) ++preservecount; - if (groups[cursorgroup].files[x].action == -1) + if (groups[cursorgroup].files[x].action == FILEACTION_DELETE) ++deletecount; } @@ -1038,9 +1039,11 @@ { for (x = 0; x < groups[cursorgroup].filecount; ++x) { - if (groups[cursorgroup].files[x].action == 0) + if (groups[cursorgroup].files[x].action == FILEACTION_UNRESOLVED || + groups[cursorgroup].files[x].action == FILEACTION_ERROR + ) { - set_file_action(&groups[cursorgroup].files[x], -1, &globaldeletiontally); + set_file_action(&groups[cursorgroup].files[x], FILEACTION_DELETE, &globaldeletiontally); ++deletecount; } } @@ -1180,7 +1183,7 @@ break; case KEY_SRIGHT: - set_file_action(&groups[cursorgroup].files[cursorfile], 1, &globaldeletiontally); + set_file_action(&groups[cursorgroup].files[cursorfile], FILEACTION_KEEP, &globaldeletiontally); format_status_left(status, L"1 file marked for preservation."); @@ -1194,12 +1197,12 @@ case KEY_SLEFT: deletecount = 0; - set_file_action(&groups[cursorgroup].files[cursorfile], -1, &globaldeletiontally); + set_file_action(&groups[cursorgroup].files[cursorfile], FILEACTION_DELETE, &globaldeletiontally); format_status_left(status, L"1 file marked for deletion."); for (x = 0; x < groups[cursorgroup].filecount; ++x) - if (groups[cursorgroup].files[x].action == -1) + if (groups[cursorgroup].files[x].action == FILEACTION_DELETE) ++deletecount; if (deletecount < groups[cursorgroup].filecount) @@ -1231,7 +1234,7 @@ break; case KEY_DC: - cmd_prune(groups, totalgroups, commandarguments, &globaldeletiontally, &totalgroups, &cursorgroup, &cursorfile, &topline, logfile, filewin, status); + cmd_prune(groups, totalgroups, commandarguments, &globaldeletiontally, &totalgroups, &cursorgroup, &cursorfile, &topline, logfile, filewin, statuswin, status); break; case KEY_RESIZE: @@ -1267,10 +1270,10 @@ switch (wch) { case '?': - if (groups[cursorgroup].files[cursorfile].action == 0) + if (groups[cursorgroup].files[cursorfile].action == FILEACTION_UNRESOLVED) break; - set_file_action(&groups[cursorgroup].files[cursorfile], 0, &globaldeletiontally); + set_file_action(&groups[cursorgroup].files[cursorfile], FILEACTION_UNRESOLVED, &globaldeletiontally); if (cursorfile < groups[cursorgroup].filecount - 1) move_to_next_file(&topline, &cursorgroup, &cursorfile, groups, filewin); @@ -1285,7 +1288,7 @@ for (x = 0; x < groups[cursorgroup].filecount; ++x) { - if (groups[cursorgroup].files[x].action == 1) + if (groups[cursorgroup].files[x].action == FILEACTION_KEEP) ++preservecount; } @@ -1294,10 +1297,10 @@ for (x = 0; x < groups[cursorgroup].filecount; ++x) { - if (groups[cursorgroup].files[x].action == 0) - set_file_action(&groups[cursorgroup].files[x], -1, &globaldeletiontally); + if (groups[cursorgroup].files[x].action == FILEACTION_UNRESOLVED) + set_file_action(&groups[cursorgroup].files[x], FILEACTION_DELETE, &globaldeletiontally); - if (groups[cursorgroup].files[x].action == -1) + if (groups[cursorgroup].files[x].action == FILEACTION_DELETE) ++deletecount; } diff -Nru fdupes-2.1.2/ncurses-interface.h fdupes-2.2.1/ncurses-interface.h --- fdupes-2.1.2/ncurses-interface.h 2020-08-12 18:04:27.000000000 +0000 +++ fdupes-2.2.1/ncurses-interface.h 2022-09-04 02:55:50.000000000 +0000 @@ -1,4 +1,4 @@ -/* FDUPES Copyright (c) 2018 Adrian Lopez +/* FDUPES Copyright (c) 2018-2022 Adrian Lopez Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files diff -Nru fdupes-2.1.2/ncurses-print.c fdupes-2.2.1/ncurses-print.c --- fdupes-2.1.2/ncurses-print.c 2020-08-12 18:04:27.000000000 +0000 +++ fdupes-2.2.1/ncurses-print.c 2022-09-04 02:55:50.000000000 +0000 @@ -1,4 +1,4 @@ -/* FDUPES Copyright (c) 2018 Adrian Lopez +/* FDUPES Copyright (c) 2018-2022 Adrian Lopez Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files diff -Nru fdupes-2.1.2/ncurses-print.h fdupes-2.2.1/ncurses-print.h --- fdupes-2.1.2/ncurses-print.h 2020-08-12 18:04:27.000000000 +0000 +++ fdupes-2.2.1/ncurses-print.h 2022-09-04 02:55:50.000000000 +0000 @@ -1,4 +1,4 @@ -/* FDUPES Copyright (c) 2018 Adrian Lopez +/* FDUPES Copyright (c) 2018-2022 Adrian Lopez Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files diff -Nru fdupes-2.1.2/ncurses-prompt.c fdupes-2.2.1/ncurses-prompt.c --- fdupes-2.1.2/ncurses-prompt.c 2020-08-12 18:04:27.000000000 +0000 +++ fdupes-2.2.1/ncurses-prompt.c 2022-09-04 02:55:50.000000000 +0000 @@ -1,4 +1,4 @@ -/* FDUPES Copyright (c) 2018 Adrian Lopez +/* FDUPES Copyright (c) 2018-2022 Adrian Lopez Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files diff -Nru fdupes-2.1.2/ncurses-prompt.h fdupes-2.2.1/ncurses-prompt.h --- fdupes-2.1.2/ncurses-prompt.h 2020-08-12 18:04:27.000000000 +0000 +++ fdupes-2.2.1/ncurses-prompt.h 2022-09-04 02:55:50.000000000 +0000 @@ -1,4 +1,4 @@ -/* FDUPES Copyright (c) 2018 Adrian Lopez +/* FDUPES Copyright (c) 2018-2022 Adrian Lopez Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files diff -Nru fdupes-2.1.2/ncurses-status.c fdupes-2.2.1/ncurses-status.c --- fdupes-2.1.2/ncurses-status.c 2020-08-12 18:04:27.000000000 +0000 +++ fdupes-2.2.1/ncurses-status.c 2022-09-04 02:55:50.000000000 +0000 @@ -1,4 +1,4 @@ -/* FDUPES Copyright (c) 2018 Adrian Lopez +/* FDUPES Copyright (c) 2018-2022 Adrian Lopez Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files diff -Nru fdupes-2.1.2/ncurses-status.h fdupes-2.2.1/ncurses-status.h --- fdupes-2.1.2/ncurses-status.h 2020-08-12 18:04:27.000000000 +0000 +++ fdupes-2.2.1/ncurses-status.h 2022-09-04 02:55:50.000000000 +0000 @@ -1,4 +1,4 @@ -/* FDUPES Copyright (c) 2018 Adrian Lopez +/* FDUPES Copyright (c) 2018-2022 Adrian Lopez Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files diff -Nru fdupes-2.1.2/positive_wcwidth.c fdupes-2.2.1/positive_wcwidth.c --- fdupes-2.1.2/positive_wcwidth.c 2020-08-12 18:04:27.000000000 +0000 +++ fdupes-2.2.1/positive_wcwidth.c 2022-09-04 02:55:50.000000000 +0000 @@ -1,4 +1,4 @@ -/* Copyright (c) 2019 Adrian Lopez +/* Copyright (c) 2019-2022 Adrian Lopez This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff -Nru fdupes-2.1.2/positive_wcwidth.h fdupes-2.2.1/positive_wcwidth.h --- fdupes-2.1.2/positive_wcwidth.h 2020-08-12 18:04:27.000000000 +0000 +++ fdupes-2.2.1/positive_wcwidth.h 2022-09-04 02:55:50.000000000 +0000 @@ -1,4 +1,4 @@ -/* Copyright (c) 2019 Adrian Lopez +/* Copyright (c) 2019-2022 Adrian Lopez This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff -Nru fdupes-2.1.2/README fdupes-2.2.1/README --- fdupes-2.1.2/README 2020-08-12 18:04:27.000000000 +0000 +++ fdupes-2.2.1/README 2022-09-04 02:55:50.000000000 +0000 @@ -8,65 +8,67 @@ -------------------------------------------------------------------- Usage: fdupes [options] DIRECTORY... - -r --recurse for every directory given follow subdirectories - encountered within - -R --recurse: for each directory given after this option follow - subdirectories encountered within (note the ':' at - the end of the option, manpage for more details) - -s --symlinks follow symlinks - -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 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 - -1 --sameline list each set of matches on a single line - -S --size show size of duplicate files - -t --time show modification time of duplicate files - -m --summarize summarize dupe information - -q --quiet hide progress indicator - -d --delete prompt user for files to preserve and delete all - others; important: under particular circumstances, - data may be lost when using this option together - with -s or --symlinks, or when specifying a - particular directory more than once; refer to the - fdupes documentation for additional information - -P --plain with --delete, use line-based prompt (as with older - versions of fdupes) instead of screen-mode interface - -N --noprompt together with --delete, preserve the first file in - each set of duplicates and delete the rest without - prompting the user - -I --immediate delete duplicates as they are encountered, without - grouping into sets; implies --noprompt - -p --permissions don't consider files with different owner/group or - permission bits as duplicates - -o --order=BY select sort order for output and deleting; by file - modification time (BY='time'; default), status - change time (BY='ctime'), or filename (BY='name') - -i --reverse reverse order while sorting - -l --log=LOGFILE log file deletion choices to LOGFILE - -v --version display fdupes version - -h --help display this help message + -r --recurse for every directory given follow subdirectories + encountered within + -R --recurse: for each directory given after this option follow + subdirectories encountered within (note the ':' at the + end of the option, manpage for more details) + -s --symlinks follow symlinks + -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 bytes + -L --maxsize=SIZE consider only files less than or equal to SIZE 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 + -1 --sameline list each set of matches on a single line + -S --size show size of duplicate files + -t --time show modification time of duplicate files + -m --summarize summarize dupe information + -q --quiet hide progress indicator + -d --delete prompt user for files to preserve and delete all + others; important: under particular circumstances, + data may be lost when using this option together + with -s or --symlinks, or when specifying a + particular directory more than once; refer to the + fdupes documentation for additional information + -D --deferconfirmation in interactive mode, defer byte-for-byte confirmation + of duplicates until just before file deletion + -P --plain with --delete, use line-based prompt (as with older + versions of fdupes) instead of screen-mode interface + -N --noprompt together with --delete, preserve the first file in + each set of duplicates and delete the rest without + prompting the user + -I --immediate delete duplicates as they are encountered, without + grouping into sets; implies --noprompt + -p --permissions don't consider files with different owner/group or + permission bits as duplicates + -o --order=BY select sort order for output and deleting; by file + modification time (BY='time'; default), status + change time (BY='ctime'), or filename (BY='name') + -i --reverse reverse order while sorting + -l --log=LOGFILE log file deletion choices to LOGFILE + -v --version display fdupes version + -h --help display this help message -Unless -1 or --sameline is specified, duplicate files are listed +Unless -1 or --sameline is specified, duplicate files are listed together in groups, each file displayed on a separate line. The groups are then separated from each other by blank lines. -When -1 or --sameline is specified, spaces and backslash characters (\) +When -1 or --sameline is specified, spaces and backslash characters (\) appearing in a filename are preceded by a backslash character. For instance, "with spaces" becomes "with\ spaces". When using -d or --delete, care should be taken to insure against accidental data loss. While no information will be immediately -lost, using this option together with -s or --symlink can lead +lost, using this option together with -s or --symlink can lead to confusing information being presented to the user when prompted for files to preserve. Specifically, a user could accidentally preserve a symlink while deleting the file it points to. A similar -problem arises when specifying a particular directory more than +problem arises when specifying a particular directory more than once. All files within that directory will be listed as their own -duplicates, leading to data loss should a user preserve a file +duplicates, leading to data loss should a user preserve a file without its "duplicate" (the file itself!). diff -Nru fdupes-2.1.2/removeifnotchanged.c fdupes-2.2.1/removeifnotchanged.c --- fdupes-2.1.2/removeifnotchanged.c 1970-01-01 00:00:00.000000000 +0000 +++ fdupes-2.2.1/removeifnotchanged.c 2022-09-04 02:55:50.000000000 +0000 @@ -0,0 +1,41 @@ +#include "removeifnotchanged.h" +#include +#include +#include + +int removeifnotchanged(const file_t *file, char **errorstring) +{ + int result; + struct stat st; + + static char *filechanged = "File contents changed during processing"; + static char *unknownerror = "Unknown error"; + + stat(file->d_name, &st); + + if (file->device != st.st_dev || + file->inode != st.st_ino || + file->ctime != st.st_ctime || + file->mtime != st.st_mtime || + file->size != st.st_size) + { + if (errorstring != 0) + *errorstring = filechanged; + + return -2; + } + else + { + result = remove(file->d_name); + + if (result != 0 && errorstring != 0) + { + *errorstring = strerror(errno); + + if (*errorstring == 0) + *errorstring = unknownerror; + } + + return result; + } +} diff -Nru fdupes-2.1.2/removeifnotchanged.h fdupes-2.2.1/removeifnotchanged.h --- fdupes-2.1.2/removeifnotchanged.h 1970-01-01 00:00:00.000000000 +0000 +++ fdupes-2.2.1/removeifnotchanged.h 2022-09-04 02:55:50.000000000 +0000 @@ -0,0 +1,8 @@ +#ifndef REMOVEIFNOTCHANGED_H +#define REMOVEIFNOTCHANGED_H + +#include "fdupes.h" + +int removeifnotchanged(const file_t *file, char **errorstring); + +#endif \ No newline at end of file diff -Nru fdupes-2.1.2/sigint.c fdupes-2.2.1/sigint.c --- fdupes-2.1.2/sigint.c 2020-08-12 18:04:27.000000000 +0000 +++ fdupes-2.2.1/sigint.c 2022-09-04 02:55:50.000000000 +0000 @@ -1,4 +1,4 @@ -/* Copyright (c) 2018 Adrian Lopez +/* Copyright (c) 2018-2022 Adrian Lopez This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff -Nru fdupes-2.1.2/sigint.h fdupes-2.2.1/sigint.h --- fdupes-2.1.2/sigint.h 2020-08-12 18:04:27.000000000 +0000 +++ fdupes-2.2.1/sigint.h 2022-09-04 02:55:50.000000000 +0000 @@ -1,4 +1,4 @@ -/* Copyright (c) 2018 Adrian Lopez +/* Copyright (c) 2018-2022 Adrian Lopez This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff -Nru fdupes-2.1.2/wcs.c fdupes-2.2.1/wcs.c --- fdupes-2.1.2/wcs.c 2020-08-12 18:04:27.000000000 +0000 +++ fdupes-2.2.1/wcs.c 2022-09-04 02:55:50.000000000 +0000 @@ -1,4 +1,4 @@ -/* Copyright (c) 2018 Adrian Lopez +/* Copyright (c) 2018-2022 Adrian Lopez This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff -Nru fdupes-2.1.2/wcs.h fdupes-2.2.1/wcs.h --- fdupes-2.1.2/wcs.h 2020-08-12 18:04:27.000000000 +0000 +++ fdupes-2.2.1/wcs.h 2022-09-04 02:55:50.000000000 +0000 @@ -1,4 +1,4 @@ -/* Copyright (c) 2018 Adrian Lopez +/* Copyright (c) 2018-2022 Adrian Lopez This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages