Projects
Mega:24.09
fdupes
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 2
View file
_service:tar_scm:fdupes.spec
Changed
@@ -3,7 +3,7 @@ Name: fdupes Epoch: 1 -Version: 2.2.1 +Version: 2.3.1 Release: 1 Summary: Identifying duplicate files residing within specified directories @@ -17,6 +17,7 @@ BuildRequires: automake BuildRequires: ncurses-devel BuildRequires: pcre2-devel +BuildRequires: sqlite-devel %description FDUPES is a program for identifying duplicate files residing within specified @@ -30,7 +31,7 @@ # From README. %{__cat} << EOF > LICENSE -FDUPES Copyright (c) 1999 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 @@ -81,6 +82,14 @@ %{_mandir}/man7/%{name}*.7* %changelog +* Tue Jul 02 2024 yaoxin <yao_xin001@hoperun.com> - 1:2.3.1-1 +- Update to 2.3.1 + * Fix buffer overflow bug in getrealpath() function. + * Add --cache option to speed up file comparisons. + * Use nanosecond precision for file times, if available. + * Fix compilation issue on OpenBSD. + * Other changes like fixing typos, wording, etc. + * Wed May 10 2023 xu_ping <707078654@qq.com> - 1:2.2.1-1 - Upgrade package to 2.2.1 version
View file
_service
Changed
@@ -2,7 +2,7 @@ <service name="tar_scm"> <param name="scm">git</param> <param name="url">git@gitee.com:src-openeuler/fdupes.git</param> - <param name="revision">master</param> + <param name="revision">openEuler-24.09</param> <param name="exclude">*</param> <param name="extract">*</param> </service>
View file
_service:tar_scm:fdupes-2.2.1.tar.gz/CHANGES -> _service:tar_scm:fdupes-2.3.1.tar.gz/CHANGES
Changed
@@ -6,6 +6,17 @@ those who've otherwise worked on that item. For a list of contributors names and identifiers please see the CONTRIBUTORS file. +Changes from 2.3.0 to 2.3.1: + + - Fix buffer overflow bug in getrealpath() function. + +Changes from 2.2.1 to 2.3.0: + + - Add --cache option to speed up file comparisons. + - Use nanosecond precision for file times, if available. + - Fix compilation issue on OpenBSD. + - Other changes like fixing typos, wording, etc. + Changes from 2.2.0 to 2.2.1: - Fix bug in code meant to skip over the current log file when --log
View file
_service:tar_scm:fdupes-2.2.1.tar.gz/Makefile.am -> _service:tar_scm:fdupes-2.3.1.tar.gz/Makefile.am
Changed
@@ -47,7 +47,19 @@ positive_wcwidth.c\ positive_wcwidth.h dist_man7_MANS = fdupes-help.7 +endif +if WITH_SQLITE +fdupes_SOURCES += getrealpath.c\ + getrealpath.h\ + sdirname.c\ + sdirname.h\ + sbasename.c\ + sbasename.h\ + xdgbase.c\ + xdgbase.h\ + hashdb.c\ + hashdb.h endif EXTRA_DIST = testdir CHANGES CONTRIBUTORS
View file
_service:tar_scm:fdupes-2.2.1.tar.gz/Makefile.in -> _service:tar_scm:fdupes-2.3.1.tar.gz/Makefile.in
Changed
@@ -109,6 +109,17 @@ @WITH_NCURSES_TRUE@ positive_wcwidth.c\ @WITH_NCURSES_TRUE@ positive_wcwidth.h +@WITH_SQLITE_TRUE@am__append_2 = getrealpath.c\ +@WITH_SQLITE_TRUE@ getrealpath.h\ +@WITH_SQLITE_TRUE@ sdirname.c\ +@WITH_SQLITE_TRUE@ sdirname.h\ +@WITH_SQLITE_TRUE@ sbasename.c\ +@WITH_SQLITE_TRUE@ sbasename.h\ +@WITH_SQLITE_TRUE@ xdgbase.c\ +@WITH_SQLITE_TRUE@ xdgbase.h\ +@WITH_SQLITE_TRUE@ hashdb.c\ +@WITH_SQLITE_TRUE@ hashdb.h + subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.ac @@ -136,7 +147,9 @@ ncurses-print.c ncurses-print.h ncurses-prompt.c \ ncurses-prompt.h ncurses-status.c ncurses-status.h \ commandidentifier.c commandidentifier.h wcs.c wcs.h \ - positive_wcwidth.c positive_wcwidth.h + positive_wcwidth.c positive_wcwidth.h getrealpath.c \ + getrealpath.h sdirname.c sdirname.h sbasename.c sbasename.h \ + xdgbase.c xdgbase.h hashdb.c hashdb.h am__dirstamp = $(am__leading_dot)dirstamp @WITH_NCURSES_TRUE@am__objects_1 = fileaction.$(OBJEXT) \ @WITH_NCURSES_TRUE@ ncurses-commands.$(OBJEXT) \ @@ -147,11 +160,14 @@ @WITH_NCURSES_TRUE@ ncurses-status.$(OBJEXT) \ @WITH_NCURSES_TRUE@ commandidentifier.$(OBJEXT) wcs.$(OBJEXT) \ @WITH_NCURSES_TRUE@ positive_wcwidth.$(OBJEXT) +@WITH_SQLITE_TRUE@am__objects_2 = getrealpath.$(OBJEXT) \ +@WITH_SQLITE_TRUE@ sdirname.$(OBJEXT) sbasename.$(OBJEXT) \ +@WITH_SQLITE_TRUE@ xdgbase.$(OBJEXT) hashdb.$(OBJEXT) am_fdupes_OBJECTS = fdupes.$(OBJEXT) errormsg.$(OBJEXT) dir.$(OBJEXT) \ log.$(OBJEXT) fmatch.$(OBJEXT) sigint.$(OBJEXT) \ flags.$(OBJEXT) confirmmatch.$(OBJEXT) \ removeifnotchanged.$(OBJEXT) mbstowcs_escape_invalid.$(OBJEXT) \ - md5/md5.$(OBJEXT) $(am__objects_1) + md5/md5.$(OBJEXT) $(am__objects_1) $(am__objects_2) fdupes_OBJECTS = $(am_fdupes_OBJECTS) fdupes_LDADD = $(LDADD) AM_V_P = $(am__v_P_@AM_V@) @@ -173,15 +189,17 @@ ./$(DEPDIR)/confirmmatch.Po ./$(DEPDIR)/dir.Po \ ./$(DEPDIR)/errormsg.Po ./$(DEPDIR)/fdupes.Po \ ./$(DEPDIR)/fileaction.Po ./$(DEPDIR)/flags.Po \ - ./$(DEPDIR)/fmatch.Po ./$(DEPDIR)/log.Po \ + ./$(DEPDIR)/fmatch.Po ./$(DEPDIR)/getrealpath.Po \ + ./$(DEPDIR)/hashdb.Po ./$(DEPDIR)/log.Po \ ./$(DEPDIR)/mbstowcs_escape_invalid.Po \ ./$(DEPDIR)/ncurses-commands.Po \ ./$(DEPDIR)/ncurses-getcommand.Po \ ./$(DEPDIR)/ncurses-interface.Po ./$(DEPDIR)/ncurses-print.Po \ ./$(DEPDIR)/ncurses-prompt.Po ./$(DEPDIR)/ncurses-status.Po \ ./$(DEPDIR)/positive_wcwidth.Po \ - ./$(DEPDIR)/removeifnotchanged.Po ./$(DEPDIR)/sigint.Po \ - ./$(DEPDIR)/wcs.Po md5/$(DEPDIR)/md5.Po + ./$(DEPDIR)/removeifnotchanged.Po ./$(DEPDIR)/sbasename.Po \ + ./$(DEPDIR)/sdirname.Po ./$(DEPDIR)/sigint.Po \ + ./$(DEPDIR)/wcs.Po ./$(DEPDIR)/xdgbase.Po md5/$(DEPDIR)/md5.Po am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) @@ -372,7 +390,8 @@ log.c log.h fmatch.c fmatch.h sigint.c 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 $(am__append_1) + mbstowcs_escape_invalid.h md5/md5.c md5/md5.h $(am__append_1) \ + $(am__append_2) dist_man1_MANS = fdupes.1 @WITH_NCURSES_TRUE@dist_man7_MANS = fdupes-help.7 EXTRA_DIST = testdir CHANGES CONTRIBUTORS @@ -498,6 +517,8 @@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fileaction.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/flags.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fmatch.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getrealpath.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hashdb.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/log.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mbstowcs_escape_invalid.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ncurses-commands.Po@am__quote@ # am--include-marker @@ -508,8 +529,11 @@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ncurses-status.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/positive_wcwidth.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/removeifnotchanged.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sbasename.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sdirname.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sigint.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wcs.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xdgbase.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@md5/$(DEPDIR)/md5.Po@am__quote@ # am--include-marker $(am__depfiles_remade): @@ -897,6 +921,8 @@ -rm -f ./$(DEPDIR)/fileaction.Po -rm -f ./$(DEPDIR)/flags.Po -rm -f ./$(DEPDIR)/fmatch.Po + -rm -f ./$(DEPDIR)/getrealpath.Po + -rm -f ./$(DEPDIR)/hashdb.Po -rm -f ./$(DEPDIR)/log.Po -rm -f ./$(DEPDIR)/mbstowcs_escape_invalid.Po -rm -f ./$(DEPDIR)/ncurses-commands.Po @@ -907,8 +933,11 @@ -rm -f ./$(DEPDIR)/ncurses-status.Po -rm -f ./$(DEPDIR)/positive_wcwidth.Po -rm -f ./$(DEPDIR)/removeifnotchanged.Po + -rm -f ./$(DEPDIR)/sbasename.Po + -rm -f ./$(DEPDIR)/sdirname.Po -rm -f ./$(DEPDIR)/sigint.Po -rm -f ./$(DEPDIR)/wcs.Po + -rm -f ./$(DEPDIR)/xdgbase.Po -rm -f md5/$(DEPDIR)/md5.Po -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ @@ -965,6 +994,8 @@ -rm -f ./$(DEPDIR)/fileaction.Po -rm -f ./$(DEPDIR)/flags.Po -rm -f ./$(DEPDIR)/fmatch.Po + -rm -f ./$(DEPDIR)/getrealpath.Po + -rm -f ./$(DEPDIR)/hashdb.Po -rm -f ./$(DEPDIR)/log.Po -rm -f ./$(DEPDIR)/mbstowcs_escape_invalid.Po -rm -f ./$(DEPDIR)/ncurses-commands.Po @@ -975,8 +1006,11 @@ -rm -f ./$(DEPDIR)/ncurses-status.Po -rm -f ./$(DEPDIR)/positive_wcwidth.Po -rm -f ./$(DEPDIR)/removeifnotchanged.Po + -rm -f ./$(DEPDIR)/sbasename.Po + -rm -f ./$(DEPDIR)/sdirname.Po -rm -f ./$(DEPDIR)/sigint.Po -rm -f ./$(DEPDIR)/wcs.Po + -rm -f ./$(DEPDIR)/xdgbase.Po -rm -f md5/$(DEPDIR)/md5.Po -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic
View file
_service:tar_scm:fdupes-2.2.1.tar.gz/README -> _service:tar_scm:fdupes-2.3.1.tar.gz/README
Changed
@@ -19,6 +19,19 @@ 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 + -c --cache speed up file comparisons by keeping track of their + signatures in a database; additional parameters may be + provided using one or more cache parameters (as below) + -x cache.OPTION supply an optional cache parameter, where OPTION is one + of the keywords below and multiple options may be + supplied via successive -x arguments: + readonly read but do not update file signatures + prune look through entire cache and delete orphaned entries + clear clear all entries from cache + vacuum reduce size of DB file, if possible + (note that the options prune, clear, and vacuum may be + employed without supplying a DIRECTORY argument, and + will take effect even if readonly is also specified) -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
View file
_service:tar_scm:fdupes-2.2.1.tar.gz/config.h.in -> _service:tar_scm:fdupes-2.3.1.tar.gz/config.h.in
Changed
@@ -3,6 +3,15 @@ /* number of bytes to read per read call */ #undef CHUNK_SIZE +/* default subdirectory for fdupes config files */ +#undef FDUPES_CACHE_DIRECTORY + +/* directory permissions for fdupes config directory */ +#undef FDUPES_CACHE_DIRECTORY_PERMISSIONS + +/* filename for fdupes hash database */ +#undef FDUPES_HASH_DATABASE_NAME + /* Define to 1 if you have the <getopt.h> header file. */ #undef HAVE_GETOPT_H @@ -15,6 +24,9 @@ /* Define to 1 if you have the <ncursesw/curses.h> header file. */ #undef HAVE_NCURSESW_CURSES_H +/* stat supports nanosecond precision */ +#undef HAVE_NSEC_TIMES + /* Define to 1 if you have the <stdint.h> header file. */ #undef HAVE_STDINT_H @@ -45,6 +57,9 @@ /* Do not compile against ncurses */ #undef NO_NCURSES +/* Do not compile against sqlite */ +#undef NO_SQLITE + /* Name of package */ #undef PACKAGE @@ -84,7 +99,7 @@ /* enable strtoll */ #undef _ISOC99_SOURCE -/* enable certain functions in wchar.h */ +/* enable certain X/Open and POSIX features */ #undef _XOPEN_SOURCE /* enable certain functions in curses.h */
View file
_service:tar_scm:fdupes-2.2.1.tar.gz/configure -> _service:tar_scm:fdupes-2.3.1.tar.gz/configure
Changed
@@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for fdupes 2.2.1. +# Generated by GNU Autoconf 2.69 for fdupes 2.3.1. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. @@ -577,8 +577,8 @@ # Identity of this package. PACKAGE_NAME='fdupes' PACKAGE_TARNAME='fdupes' -PACKAGE_VERSION='2.2.1' -PACKAGE_STRING='fdupes 2.2.1' +PACKAGE_VERSION='2.3.1' +PACKAGE_STRING='fdupes 2.3.1' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -622,6 +622,8 @@ am__EXEEXT_TRUE LTLIBOBJS LIBOBJS +WITH_SQLITE_FALSE +WITH_SQLITE_TRUE WITH_NCURSES_FALSE WITH_NCURSES_TRUE NCURSES_LIBS @@ -719,8 +721,9 @@ ac_user_opts=' enable_option_checking enable_silent_rules -with_ncurses enable_dependency_tracking +with_ncurses +with_sqlite ' ac_precious_vars='build_alias host_alias @@ -1286,7 +1289,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures fdupes 2.2.1 to adapt to many kinds of systems. +\`configure' configures fdupes 2.3.1 to adapt to many kinds of systems. Usage: $0 OPTION... VAR=VALUE... @@ -1353,7 +1356,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of fdupes 2.2.1:";; + short | recursive ) echo "Configuration of fdupes 2.3.1:";; esac cat <<\_ACEOF @@ -1372,6 +1375,7 @@ --with-PACKAGE=ARG use PACKAGE ARG=yes --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --without-ncurses Do not use ncurses interface + --without-sqlite Do not use sqlite database Some influential environment variables: PKG_CONFIG path to pkg-config utility @@ -1458,7 +1462,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -fdupes configure 2.2.1 +fdupes configure 2.3.1 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1756,7 +1760,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by fdupes $as_me 2.2.1, which was +It was created by fdupes $as_me 2.3.1, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2620,7 +2624,7 @@ # Define the identity of the package. PACKAGE='fdupes' - VERSION='2.2.1' + VERSION='2.3.1' cat >>confdefs.h <<_ACEOF @@ -2840,13 +2844,6 @@ fi fi - -# Check whether --with-ncurses was given. -if test "${with_ncurses+set}" = set; then : - withval=$with_ncurses; -fi - - DEPDIR="${am__leading_dot}deps" ac_config_commands="$ac_config_commands depfiles" @@ -3887,6 +3884,47 @@ +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #define _XOPEN_SOURCE 700 + #include <sys/types.h> + #include <sys/stat.h> + #include <unistd.h> + +int +main () +{ + + struct stat st; + st.st_ctim.tv_nsec = 0; + st.st_mtim.tv_nsec = 0; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +$as_echo "#define HAVE_NSEC_TIMES 1" >>confdefs.h + + +$as_echo "#define _XOPEN_SOURCE 700" >>confdefs.h + + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +# +# NCURSES library +# + +# Check whether --with-ncurses was given. +if test "${with_ncurses+set}" = set; then : + withval=$with_ncurses; +fi + + ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' @@ -4297,7 +4335,6 @@ done - if test x"$with_ncurses" != x"no"; then : pkg_failed=no @@ -4603,7 +4640,7 @@ LIBS="$LIBS $NCURSES_LIBS" fi -$as_echo "#define _XOPEN_SOURCE /**/" >>confdefs.h +$as_echo "#define _XOPEN_SOURCE 700" >>confdefs.h $as_echo "#define _XOPEN_SOURCE_EXTENDED /**/" >>confdefs.h @@ -4688,6 +4725,91 @@ fi +# +# SQLITE library +# + +# Check whether --with-sqlite was given. +if test "${with_sqlite+set}" = set; then : + withval=$with_sqlite; +fi + + +if test x"$with_sqlite" != x"no"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing sqlite3_prepare_v2" >&5 +$as_echo_n "checking for library containing sqlite3_prepare_v2... " >&6; } +if ${ac_cv_search_sqlite3_prepare_v2+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char sqlite3_prepare_v2 (); +int +main () +{ +return sqlite3_prepare_v2 (); + ; + return 0; +} +_ACEOF +for ac_lib in '' sqlite3; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_sqlite3_prepare_v2=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_sqlite3_prepare_v2+:} false; then : + break +fi +done +if ${ac_cv_search_sqlite3_prepare_v2+:} false; then : + +else + ac_cv_search_sqlite3_prepare_v2=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_sqlite3_prepare_v2" >&5 +$as_echo "$ac_cv_search_sqlite3_prepare_v2" >&6; } +ac_res=$ac_cv_search_sqlite3_prepare_v2 +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +else + as_fn_error $? "sqlite3 library not found" "$LINENO" 5 +fi + +else + +$as_echo "#define NO_SQLITE /**/" >>confdefs.h + + +fi + + if test x"$with_sqlite" != x"no"; then + WITH_SQLITE_TRUE= + WITH_SQLITE_FALSE='#' +else + WITH_SQLITE_TRUE='#' + WITH_SQLITE_FALSE= +fi + + 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"` transformed_manpage_name=`echo "${PACKAGE_NAME}-help"|sed -e "${unescaped_program_transform_name}"` @@ -4712,6 +4834,16 @@ $as_echo "#define INPUT_SIZE 256" >>confdefs.h + +$as_echo "#define FDUPES_CACHE_DIRECTORY \"fdupes\"" >>confdefs.h + + +$as_echo "#define FDUPES_CACHE_DIRECTORY_PERMISSIONS 0700" >>confdefs.h + + +$as_echo "#define FDUPES_HASH_DATABASE_NAME \"hash.db\"" >>confdefs.h + + ac_config_files="$ac_config_files Makefile" ac_ext=c @@ -5577,6 +5709,10 @@ as_fn_error $? "conditional \"WITH_NCURSES\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi +if test -z "${WITH_SQLITE_TRUE}" && test -z "${WITH_SQLITE_FALSE}"; then + as_fn_error $? "conditional \"WITH_SQLITE\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then as_fn_error $? "conditional \"am__fastdepCC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 @@ -5978,7 +6114,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by fdupes $as_me 2.2.1, which was +This file was extended by fdupes $as_me 2.3.1, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -6044,7 +6180,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/\\""\`\$/\\\\&/g'`" ac_cs_version="\\ -fdupes config.status 2.2.1 +fdupes config.status 2.3.1 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\"
View file
_service:tar_scm:fdupes-2.2.1.tar.gz/configure.ac -> _service:tar_scm:fdupes-2.3.1.tar.gz/configure.ac
Changed
@@ -1,4 +1,4 @@ -AC_INIT(fdupes, 2.2.1) +AC_INIT(fdupes, 2.3.1) AM_INIT_AUTOMAKE(foreign subdir-objects) @@ -8,17 +8,33 @@ PKG_PROG_PKG_CONFIG +AC_COMPILE_IFELSE(AC_LANG_PROGRAM( + #define _XOPEN_SOURCE 700 + #include <sys/types.h> + #include <sys/stat.h> + #include <unistd.h> +, + struct stat st; + st.st_ctim.tv_nsec = 0; + st.st_mtim.tv_nsec = 0; +), + AC_DEFINE(HAVE_NSEC_TIMES, 1, stat supports nanosecond precision) + AC_DEFINE(_XOPEN_SOURCE, 700, enable certain X/Open and POSIX features) +) + +# +# NCURSES library +# AC_ARG_WITH(ncurses, AS_HELP_STRING(--without-ncurses, Do not use ncurses interface)) AC_CHECK_HEADERS(getopt.h ncursesw/curses.h) - AS_IF(test x"$with_ncurses" != x"no", PKG_CHECK_MODULES(NCURSES, ncursesw, LIBS="$LIBS $NCURSES_LIBS", AC_SEARCH_LIBS(wget_wch, ncursesw ncurses curses, , AC_ERROR(ncurses library not found (or lacks wide character support))) AC_SEARCH_LIBS(keypad, ncursesw tinfow ncurses tinfo curses, , AC_ERROR(ncurses library not found (lacks keypad support))) ) - AC_DEFINE(_XOPEN_SOURCE, , enable certain functions in wchar.h) + AC_DEFINE(_XOPEN_SOURCE, 700, enable certain X/Open and POSIX features) AC_DEFINE(_XOPEN_SOURCE_EXTENDED, , enable certain functions in curses.h) AC_DEFINE(_ISOC99_SOURCE, , enable strtoll) AC_SEARCH_LIBS(pcre2_match_32, pcre2-32, , AC_ERROR(pcre2 library not found)) @@ -29,6 +45,19 @@ AM_CONDITIONAL(WITH_NCURSES, test x"$with_ncurses" != x"no") +# +# SQLITE library +# +AC_ARG_WITH(sqlite, AS_HELP_STRING(--without-sqlite, Do not use sqlite database)) + +AS_IF(test x"$with_sqlite" != x"no", + AC_SEARCH_LIBS(sqlite3_prepare_v2, sqlite3, , AC_ERROR(sqlite3 library not found)), + + AC_DEFINE(NO_SQLITE, , Do not compile against sqlite) + ) + +AM_CONDITIONAL(WITH_SQLITE, test x"$with_sqlite" != 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"` transformed_manpage_name=`echo "${PACKAGE_NAME}-help"|sed -e "${unescaped_program_transform_name}"` @@ -41,6 +70,10 @@ 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_DEFINE(FDUPES_CACHE_DIRECTORY, "fdupes", default subdirectory for fdupes config files) +AC_DEFINE(FDUPES_CACHE_DIRECTORY_PERMISSIONS, 0700, directory permissions for fdupes config directory) +AC_DEFINE(FDUPES_HASH_DATABASE_NAME, "hash.db", filename for fdupes hash database) + AC_CONFIG_FILES(Makefile) AC_PROG_CC AC_OUTPUT
View file
_service:tar_scm:fdupes-2.2.1.tar.gz/confirmmatch.c -> _service:tar_scm:fdupes-2.3.1.tar.gz/confirmmatch.c
Changed
@@ -20,7 +20,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "config.h" +#include "sigint.h" #include "confirmmatch.h" +#include <stdlib.h> #include <memory.h> /* Do a bit-for-bit comparison in case two different files produce the @@ -37,6 +39,12 @@ fseek(file2, 0, SEEK_SET); do { + if (got_sigint) { + fclose(file1); + fclose(file2); + exit(0); + } + r1 = fread(c1, sizeof(unsigned char), sizeof(c1), file1); r2 = fread(c2, sizeof(unsigned char), sizeof(c2), file2);
View file
_service:tar_scm:fdupes-2.2.1.tar.gz/fdupes.1 -> _service:tar_scm:fdupes-2.3.1.tar.gz/fdupes.1
Changed
@@ -19,89 +19,119 @@ .SH OPTIONS .TP .B -r --recurse -for every directory given follow subdirectories encountered within +For every directory given follow subdirectories encountered within. .TP .B -R --recurse: -for each directory given after this option follow subdirectories +For each directory given after this option follow subdirectories encountered within (note the ':' at the end of option; see the -Examples section below for further explanation) +Examples section below for further explanation). .TP .B -s --symlinks -follow symlinked directories +Follow symlinked directories. .TP .B -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 +Normally, when two or more files point to the same disk area they are +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 in bytes +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 in bytes +.B -L --maxsize\fR=\fISIZE\fR +Consider only files less than or equal to SIZE in bytes. +.TP +.B -c --cache +Speed up file comparisons by keeping track of their signatures in a +database; additional parameters may be provided using one or more +cache parameters (as indicated below). Please note that this option +may not be available on some systems. +.TP +.B -x cache.\fIOPTION\fR +Supply an optional cache parameter, where OPTION is one of the keywords +below and multiple options may be supplied via successive -x arguments: + + \fIreadonly\fR + read but do not update file signatures + + \fIprune\fR + look through entire cache and delete orphaned entries + + \fIclear\fR + clear all entries from cache + + \fIvacuum\fR + reduce size of DB file, if possible + +The options prune, clear, and vacuum may be employed without +supplying a DIRECTORY argument, and will take effect even if readonly +is also specified. The order of operations is always clear, prune, +update signatures (unless readonly), and vacuum. .TP .B -n --noempty -exclude zero-length files from consideration +Exclude zero-length files from consideration. .TP .B -A --nohidden -exclude hidden files from consideration +Exclude hidden files from consideration. .TP .B -f --omitfirst -omit the first file in each set of matches +Omit the first file in each set of matches. .TP .B -1 --sameline -list each set of matches on a single line +List each set of matches on a single line. .TP .B -S --size -show size of duplicate files +Show size of duplicate files. .TP .B -t --time -show modification time of duplicate files +Show modification time of duplicate files. .TP .B -m --summarize -summarize duplicate file information +Summarize duplicate file information. .TP .B -q --quiet -hide progress indicator +Hide progress indicator. .TP .B -d --delete -prompt user for files to preserve, deleting all others (see +Prompt user for files to preserve, deleting all others (see .B CAVEATS -below) +below). .TP .B -D --deferconfirmation -in interactive mode, defer byte-for-byte confirmation of -duplicates until just before file deletion +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 +With --delete, use a line-based prompt (as with older versions of +fdupes) instead of the new screen-mode interface. On installations +where the screen-mode interface is not supported, fdupes will +default to a line-based prompt. .TP .B -N --noprompt -when used together with \-\-delete, preserve the first file in each -set of duplicates and delete the others without prompting the user +When used together with \-\-delete, preserve the first file in each +set of duplicates and delete the others without prompting the user. .TP .B -I --immediate -delete duplicates as they are encountered, without -grouping into sets; implies --noprompt +Delete duplicates as they are encountered, without +grouping into sets; implies --noprompt. .TP .B -p --permissions -don't consider files with different owner/group or permission bits as duplicates +Don't consider files with different owner/group or permission bits as duplicates. .TP .B -o --order\fR=\fIWORD\fR -order files according to WORD: -time - sort by modification time, ctime - sort by status change time, name - sort by filename +Order files according to WORD: +time - sort by modification time, ctime - sort by status change time, name - sort by +filename. .TP .B -i --reverse -reverse order while sorting +Reverse order while sorting. .TP .B -l --log\fR=\fILOGFILE\fR -log file deletion choices to LOGFILE +Log file deletion choices to LOGFILE. .TP .B -v --version -display fdupes version +Display fdupes version. .TP .B -h --help -displays help +Display help. .SH NOTES Unless .B -1 @@ -121,10 +151,10 @@ .SH EXAMPLES .TP .B fdupes a --recurse: b -will follow subdirectories under b, but not those under a. +Will follow subdirectories under b, but not those under a. .TP .B fdupes a --recurse b -will follow subdirectories under both a and b. +Will follow subdirectories under both a and b. .SH CAVEATS When using
View file
_service:tar_scm:fdupes-2.2.1.tar.gz/fdupes.c -> _service:tar_scm:fdupes-2.3.1.tar.gz/fdupes.c
Changed
@@ -19,6 +19,11 @@ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +// todo: indicate error when options that dont ignore DIRECTORY are specified? +// todo: detect entries that have changed FROM S_ISREG to S_ISDIR? +// todo: got_sigint path bypasses free()-ing of certain structures +// todo: free oldargv on error + #include "config.h" #include <stdio.h> #include <stdarg.h> @@ -50,18 +55,30 @@ #include "sigint.h" #include "flags.h" #include "removeifnotchanged.h" +#ifndef NO_SQLITE +#define FDUPES_DATABASE_DIRECTORY FDUPES_CACHE_DIRECTORY "/" FDUPES_HASH_DATABASE_NAME + #include "hashdb.h" + #include "getrealpath.h" + #include "xdgbase.h" +#endif + +char *program_name; long long minsize = -1; long long maxsize = -1; +#ifndef NO_SQLITE +sqlite3 *db; +#endif + +struct log_info *loginfo; + typedef enum { ORDER_MTIME = 0, ORDER_CTIME, ORDER_NAME } ordertype_t; -char *program_name; - ordertype_t ordertype = ORDER_MTIME; #define MD5_DIGEST_LENGTH 16 @@ -165,8 +182,8 @@ } /* Find the first non-option argument after specified option. */ -int nonoptafter(char *option, int argc, char **oldargv, - char **newargv, int optind) +int nonoptafter(char *option, int argc, char **oldargv, + char **newargv, int optind, int *foundoption) { int x; int targetind; @@ -174,7 +191,9 @@ int startat = 1; targetind = findarg(option, 1, argc, oldargv); - + + *foundoption = targetind < argc; + for (x = optind; x < argc; x++) { testind = findarg(newargvx, startat, argc, oldargv); if (testind > targetind) return x; @@ -191,8 +210,56 @@ file->device = info->st_dev; file->ctime = info->st_ctime; file->mtime = info->st_mtime; +#ifdef HAVE_NSEC_TIMES + file->ctime_nsec = info->st_ctim.tv_nsec; + file->mtime_nsec = info->st_mtim.tv_nsec; +#else + file->ctime_nsec = 0; + file->mtime_nsec = 0; +#endif +} + +#ifndef NO_SQLITE +int delist_hash_if_orphaned(const sqlite3_int64 directoryid, const char *filename, const char *directory) +{ + const char *path; + char *fullpath; + + if (got_sigint) + return 0; + + fullpath = malloc(strlen(directory) + strlen(filename) + 2); + if (fullpath == 0) { + errormsg("out of memory!\n"); + exit(1); + } + + strcpy(fullpath, directory); + strcat(fullpath, "/"); + strcat(fullpath, filename); + + if (access(fullpath, F_OK) != 0) + hashdb_deletehash(db, directoryid, filename); + + free(fullpath); + + return 1; } +int delist_directory_if_missing(const sqlite3_int64 directoryid, const char *name, const char *full_path, const sqlite3_int64 parent) +{ + struct stat st; + + if (got_sigint) + return 0; + + if (lstat(full_path, &st) != 0 || !S_ISDIR(st.st_mode)) + return hashdb_deletedirectory(db, directoryid); + + return 1; +} +#endif + int grokdir(char *dir, file_t **filelistp, struct stat *logfile_status) { DIR *cd; @@ -200,11 +267,16 @@ struct dirent *dirinfo; int lastchar; int filecount = 0; + int filesadded; struct stat info; struct stat linfo; static int progress = 0; static char indicator = "-\\|/"; char *fullname, *name; + char *fullpath = 0; +#ifndef NO_SQLITE + sqlite3_int64 pathid = 0; +#endif cd = opendir(dir); @@ -213,7 +285,26 @@ return 0; } +#ifndef NO_SQLITE + if (db != 0) { + fullpath = getrealpath(dir, 0); + + if (fullpath && !ISFLAG(flags, F_READONLYCACHE)) { + if (hashdb_getdirectoryid(db, fullpath, &pathid)) { + hashdb_foreachdirectory(db, &pathid, delist_directory_if_missing); + hashdb_foreachhash(db, &pathid, delist_hash_if_orphaned); + } + } + } +#endif + while ((dirinfo = readdir(cd)) != NULL) { + if (got_sigint) { + closedir(cd); + printf("\n"); + exit(0); + } + if (strcmp(dirinfo->d_name, ".") && strcmp(dirinfo->d_name, "..")) { if (!ISFLAG(flags, F_HIDEPROGRESS)) { fprintf(stderr, "\rBuilding file list %c ", indicatorprogress); @@ -296,8 +387,16 @@ } if (S_ISDIR(info.st_mode)) { - if (ISFLAG(flags, F_RECURSE) && (ISFLAG(flags, F_FOLLOWLINKS) || !S_ISLNK(linfo.st_mode))) - filecount += grokdir(newfile->d_name, filelistp, logfile_status); + if (ISFLAG(flags, F_RECURSE) && (ISFLAG(flags, F_FOLLOWLINKS) || !S_ISLNK(linfo.st_mode))) + { + filesadded = grokdir(newfile->d_name, filelistp, logfile_status); + filecount += filesadded; + +#ifndef NO_SQLITE + if (db != 0 && pathid == 0 && !ISFLAG(flags, F_READONLYCACHE) && filesadded > 0) + hashdb_savedirectory(db, fullpath); +#endif + } free(newfile->d_name); free(newfile); } else { @@ -313,6 +412,9 @@ } } + if (fullpath) + free(fullpath); + closedir(cd); return filecount; @@ -322,12 +424,18 @@ { off_t toread; md5_state_t state; - static md5_byte_t digestMD5_DIGEST_LENGTH; + md5_byte_t *digest; static md5_byte_t chunkCHUNK_SIZE; FILE *file; - + + digest = (md5_byte_t*) malloc(MD5_DIGEST_LENGTH * sizeof(md5_byte_t)); + if (digest == NULL) { + errormsg("out of memory\n"); + exit(1); + } + md5_init(&state); - + if (max_read != 0 && fsize > max_read) fsize = max_read; @@ -336,8 +444,14 @@ errormsg("error opening file %s\n", filename); return NULL; } - + while (fsize > 0) { + if (got_sigint) { + fclose(file); + printf("\n"); + exit(0); + } + toread = (fsize >= CHUNK_SIZE) ? CHUNK_SIZE : fsize; if (fread(chunk, toread, 1, file) != 1) { errormsg("error reading from file %s\n", filename); @@ -546,7 +660,7 @@ file_t **checkmatch(filetree_t **root, filetree_t *checktree, file_t *file) { int cmpresult; - md5_byte_t *crcsignature; + char *fullpath; if (ISFLAG(flags, F_CONSIDERHARDLINKS)) { @@ -565,10 +679,10 @@ if (is_hardlink(checktree, file)) return NULL; } - + if (file->size < checktree->file->size) cmpresult = -1; - else + else if (file->size > checktree->file->size) cmpresult = 1; else if (ISFLAG(flags, F_PERMISSIONS) && @@ -576,60 +690,68 @@ cmpresult = -1; else { if (checktree->file->crcpartial == NULL) { - crcsignature = getcrcpartialsignature(checktree->file->d_name, checktree->file->size); - if (crcsignature == NULL) { - errormsg ("cannot read file %s\n", checktree->file->d_name); - return NULL; - } +#ifndef NO_SQLITE + if (ISFLAG(flags, F_CACHESIGNATURES)) + hashdb_loadhash(db, checktree->file, &checktree->file->crcpartial, &checktree->file->crcsignature); +#endif - checktree->file->crcpartial = (md5_byte_t*) malloc(MD5_DIGEST_LENGTH * sizeof(md5_byte_t)); - if (checktree->file->crcpartial == NULL) { - errormsg("out of memory\n"); - exit(1); + if (checktree->file->crcpartial == NULL) + { + checktree->file->crcpartial = getcrcpartialsignature(checktree->file->d_name, checktree->file->size); + if (checktree->file->crcpartial == NULL) { + errormsg ("cannot read file %s\n", checktree->file->d_name); + return NULL; + } + +#ifndef NO_SQLITE + if (ISFLAG(flags, F_CACHESIGNATURES) && !ISFLAG(flags, F_READONLYCACHE)) + hashdb_savehash(db, checktree->file, checktree->file->crcpartial, checktree->file->crcsignature); +#endif } - md5copy(checktree->file->crcpartial, crcsignature); } if (file->crcpartial == NULL) { - crcsignature = getcrcpartialsignature(file->d_name, file->size); - if (crcsignature == NULL) { - errormsg ("cannot read file %s\n", file->d_name); - return NULL; - } +#ifndef NO_SQLITE + if (ISFLAG(flags, F_CACHESIGNATURES)) + hashdb_loadhash(db, file, &file->crcpartial, &file->crcsignature); +#endif - file->crcpartial = (md5_byte_t*) malloc(MD5_DIGEST_LENGTH * sizeof(md5_byte_t)); - if (file->crcpartial == NULL) { - errormsg("out of memory\n"); - exit(1); + if (file->crcpartial == NULL) + { + file->crcpartial = getcrcpartialsignature(file->d_name, file->size); + if (file->crcpartial == NULL) { + errormsg ("cannot read file %s\n", file->d_name); + return NULL; + } + +#ifndef NO_SQLITE + if (ISFLAG(flags, F_CACHESIGNATURES) && !ISFLAG(flags, F_READONLYCACHE)) + hashdb_savehash(db, file, file->crcpartial, file->crcsignature); +#endif } - md5copy(file->crcpartial, crcsignature); } cmpresult = md5cmp(file->crcpartial, checktree->file->crcpartial); if (cmpresult == 0) { if (checktree->file->crcsignature == NULL) { - crcsignature = getcrcsignature(checktree->file->d_name, checktree->file->size); - if (crcsignature == NULL) return NULL; - - checktree->file->crcsignature = (md5_byte_t*) malloc(MD5_DIGEST_LENGTH * sizeof(md5_byte_t)); - if (checktree->file->crcsignature == NULL) { - errormsg("out of memory\n"); - exit(1); - } - md5copy(checktree->file->crcsignature, crcsignature); + checktree->file->crcsignature = getcrcsignature(checktree->file->d_name, checktree->file->size); + if (checktree->file->crcsignature == NULL) + return NULL; +#ifndef NO_SQLITE + if (ISFLAG(flags, F_CACHESIGNATURES) && !ISFLAG(flags, F_READONLYCACHE)) + hashdb_savehash(db, checktree->file, checktree->file->crcpartial, checktree->file->crcsignature); +#endif } if (file->crcsignature == NULL) { - crcsignature = getcrcsignature(file->d_name, file->size); - if (crcsignature == NULL) return NULL; - - file->crcsignature = (md5_byte_t*) malloc(MD5_DIGEST_LENGTH * sizeof(md5_byte_t)); - if (file->crcsignature == NULL) { - errormsg("out of memory\n"); - exit(1); - } - md5copy(file->crcsignature, crcsignature); + file->crcsignature = getcrcsignature(file->d_name, file->size); + if (file->crcsignature == NULL) + return NULL; +#ifndef NO_SQLITE + if (ISFLAG(flags, F_CACHESIGNATURES) && !ISFLAG(flags, F_READONLYCACHE)) + hashdb_savehash(db, file, file->crcpartial, file->crcsignature); +#endif } cmpresult = md5cmp(file->crcsignature, checktree->file->crcsignature); @@ -650,7 +772,7 @@ registerfile(&(checktree->right), file); return NULL; } - } else + } else { return &checktree->file; } @@ -805,6 +927,7 @@ FILE *file1; FILE *file2; int ismatch; + char *deletepath; char *errorstring; curfile = files; @@ -841,7 +964,10 @@ if (logfile != 0) loginfo = log_open(logfile, &log_error); - register_sigint_handler(); +#ifndef NO_SQLITE + if (!prompt) + hashdb_begintransaction(db); +#endif while (files) { if (files->hasdupes) { @@ -896,15 +1022,10 @@ if (got_sigint) { - if (loginfo) - log_close(loginfo); - free(dupelist); free(preserve); free(preservestr); - printf("\n"); - exit(0); } } @@ -931,9 +1052,6 @@ if (strcmp(preservestr, "q\n") == 0 || strcmp(preservestr, "quit\n") == 0) { - if (loginfo) - log_close(loginfo); - free(dupelist); free(preserve); free(preservestr); @@ -966,6 +1084,11 @@ if (loginfo) log_begin_set(loginfo); +#ifndef NO_SQLITE + if (prompt) + hashdb_begintransaction(db); +#endif + for (x = 1; x <= counter; x++) { if (preservex) { @@ -1010,6 +1133,20 @@ if (removeifnotchanged(dupelistx, &errorstring) == 0) { printf(" - %s\n", dupelistx->d_name); +#ifndef NO_SQLITE + if (db) + { + deletepath = getrealpath(dupelistx->d_name, GETREALPATH_IGNORE_MISSING_BASENAME); + if (deletepath != 0) + { + if (!ISFLAG(flags, F_READONLYCACHE)) + hashdb_deletehashforpath(db, deletepath); + + free(deletepath); + } + } +#endif + if (loginfo) log_file_deleted(loginfo, dupelistx->d_name); } @@ -1034,13 +1171,25 @@ if (loginfo) log_end_set(loginfo); + +#ifndef NO_SQLITE + if (prompt) + hashdb_committransaction(db); +#endif } files = files->next; } - if (loginfo) +#ifndef NO_SQLITE + if (!prompt) + hashdb_committransaction(db); +#endif + + if (loginfo) { log_close(loginfo); + loginfo = 0; + } free(dupelist); free(preserve); @@ -1061,6 +1210,10 @@ return !ISFLAG(flags, F_REVERSE) ? -1 : 1; else if (f1->ctime > f2->ctime) return !ISFLAG(flags, F_REVERSE) ? 1 : -1; + else if (f1->ctime_nsec < f2->ctime_nsec) + return !ISFLAG(flags, F_REVERSE) ? -1 : 1; + else if (f1->ctime_nsec > f2->ctime_nsec) + return !ISFLAG(flags, F_REVERSE) ? 1 : -1; return 0; } @@ -1071,6 +1224,10 @@ return !ISFLAG(flags, F_REVERSE) ? -1 : 1; else if (f1->mtime > f2->mtime) return !ISFLAG(flags, F_REVERSE) ? 1 : -1; + else if (f1->mtime_nsec < f2->mtime_nsec) + return !ISFLAG(flags, F_REVERSE) ? -1 : 1; + else if (f1->mtime_nsec > f2->mtime_nsec) + return !ISFLAG(flags, F_REVERSE) ? 1 : -1; else return sort_pairs_by_ctime(f1, f2); } @@ -1132,6 +1289,7 @@ { file_t *to_keep; file_t *to_delete; + char *deletepath; char *errorstring; if (comparef(duplicate, *existing) >= 0) @@ -1162,6 +1320,20 @@ if (removeifnotchanged(to_delete, &errorstring) == 0) { printf(" - %s\n", to_delete->d_name); +#ifndef NO_SQLITE + if (db) + { + deletepath = getrealpath(to_delete->d_name, GETREALPATH_IGNORE_MISSING_BASENAME); + if (deletepath != 0) + { + if (!ISFLAG(flags, F_READONLYCACHE)) + hashdb_deletehashforpath(db, deletepath); + + free(deletepath); + } + } +#endif + if (loginfo) log_file_deleted(loginfo, to_delete->d_name); } else { @@ -1205,6 +1377,21 @@ printf(" option will change this behavior\n"); printf(" -G --minsize=SIZE consider only files greater than or equal to SIZE bytes\n"); printf(" -L --maxsize=SIZE consider only files less than or equal to SIZE bytes\n"); +#ifndef NO_SQLITE + printf(" -c --cache speed up file comparisons by keeping track of their\n"); + printf(" signatures in a database; additional parameters may be\n"); + printf(" provided using one or more cache parameters (as below)\n"); + printf(" -x cache.OPTION supply an optional cache parameter, where OPTION is one\n"); + printf(" of the keywords below and multiple options may be\n"); + printf(" supplied via successive -x arguments:\n"); + printf(" readonly read but do not update file signatures\n"); + printf(" prune look through entire cache and delete orphaned entries\n"); + printf(" clear clear all entries from cache\n"); + printf(" vacuum reduce size of DB file, if possible\n"); + printf(" (note that the options prune, clear, and vacuum may be\n"); + printf(" employed without supplying a DIRECTORY argument, and\n"); + printf(" will take effect even if readonly is also specified)\n"); +#endif printf(" -n --noempty exclude zero-length files from consideration\n"); printf(" -A --nohidden exclude hidden files from consideration\n"); printf(" -f --omitfirst omit the first file in each set of matches\n"); @@ -1244,6 +1431,32 @@ #endif } +void close_log_on_exit() +{ + if (loginfo) { + log_close(loginfo); + loginfo = 0; + } +} + +#ifndef NO_SQLITE +void close_db_on_exit() +{ + if (db != 0) + { + if (!sqlite3_get_autocommit(db)) + hashdb_committransaction(db); + + if (ISFLAG(flags, F_VACUUMCACHE) && !got_sigint) + hashdb_vacuum(db); + + hashdb_close(db); + + db = 0; + } +} +#endif + int main(int argc, char **argv) { int x; int opt; @@ -1257,12 +1470,14 @@ int progress = 0; char **oldargv; int firstrecurse; + int foundoption; char *logfile = 0; - struct log_info *loginfo = NULL; int log_error; struct stat logfile_status; char *endptr; - + char *cachehome; + char *cachepath; + #ifdef HAVE_GETOPT_H static struct option long_options = { @@ -1292,6 +1507,7 @@ { "reverse", 0, 0, 'i' }, { "log", 1, 0, 'l' }, { "deferconfirmation", 0, 0, 'D' }, + { "cache", 0, 0, 'c' }, { 0, 0, 0, 0 } }; #define GETOPT getopt_long @@ -1305,7 +1521,7 @@ oldargv = cloneargs(argc, argv); - while ((opt = GETOPT(argc, argv, "frRq1StsHG:L:nAdPvhNImpo:il:D" + while ((opt = GETOPT(argc, argv, "frRq1StsHG:L:nAdPvhNImpo:il:Dcx:" #ifdef HAVE_GETOPT_H , long_options, NULL #endif @@ -1405,17 +1621,62 @@ case 'D': SETFLAG(flags, F_DEFERCONFIRMATION); break; + case 'c': + SETFLAG(flags, F_CACHESIGNATURES); + break; + case 'x': + if (strcmp("cache.readonly", optarg) == 0) + SETFLAG(flags, F_READONLYCACHE); + else if (strcmp("cache.prune", optarg) == 0) + SETFLAG(flags, F_PRUNECACHE); + else if (strcmp("cache.clear", optarg) == 0) + SETFLAG(flags, F_CLEARCACHE); + else if (strcmp("cache.vacuum", optarg) == 0) + SETFLAG(flags, F_VACUUMCACHE); + else if (strcmp("summarize.faster", optarg) == 0) + SETFLAG(flags, F_SUMMARIZEFASTER); + else { + errormsg("unrecognized option '-x %s'\n", optarg); + fprintf(stderr, "Try `fdupes --help' for more information.\n"); + exit(1); + } + break; default: fprintf(stderr, "Try `fdupes --help' for more information.\n"); exit(1); } } - if (optind >= argc) { + if (optind >= argc && !(ISFLAG(flags, F_CLEARCACHE) || ISFLAG(flags, F_PRUNECACHE) || ISFLAG(flags, F_VACUUMCACHE))) { errormsg("no directories specified\n"); exit(1); } +#ifdef NO_SQLITE + if ( + ISFLAG(flags, F_CACHESIGNATURES) || + ISFLAG(flags, F_CLEARCACHE) || + ISFLAG(flags, F_PRUNECACHE) || + ISFLAG(flags, F_READONLYCACHE) || + ISFLAG(flags, F_VACUUMCACHE) + ) { + errormsg("file signature database is not supported in this fdupes build\n"); + exit(1); + } +#else + if (!ISFLAG(flags, F_CACHESIGNATURES)) { + if ( + ISFLAG(flags, F_CLEARCACHE) || + ISFLAG(flags, F_PRUNECACHE) || + ISFLAG(flags, F_READONLYCACHE) || + ISFLAG(flags, F_VACUUMCACHE) + ) { + errormsg("-xcache parameters must be accompanied by --cache option\n"); + exit(1); + } + } +#endif + if (ISFLAG(flags, F_RECURSE) && ISFLAG(flags, F_RECURSEAFTER)) { errormsg("options --recurse and --recurse: are not compatible\n"); exit(1); @@ -1432,11 +1693,15 @@ exit(1); } - if (!ISFLAG(flags, F_DELETEFILES)) + if (!ISFLAG(flags, F_DELETEFILES)) { logfile = 0; + loginfo = 0; + } if (logfile != 0) { + atexit(close_log_on_exit); + loginfo = log_open(logfile, &log_error); if (loginfo == 0) { @@ -1455,16 +1720,81 @@ } } +#ifndef NO_SQLITE + if (ISFLAG(flags, F_CACHESIGNATURES)) { + cachehome = getcachehome(1); + if (cachehome == 0) + { + errormsg("could not open cache directory.\n"); + exit(1); + } + + cachepath = malloc(strlen(cachehome) + strlen(FDUPES_DATABASE_DIRECTORY) + 2); + if (cachepath == 0) + { + free(cachehome); + errormsg("could not open cache directory.\n"); + exit(1); + } + + strcpy(cachepath, cachehome); + strcat(cachepath, "/"); + strcat(cachepath, FDUPES_CACHE_DIRECTORY); + + mkdir(cachepath, FDUPES_CACHE_DIRECTORY_PERMISSIONS); + + strcpy(cachepath, cachehome); + strcat(cachepath, "/"); + strcat(cachepath, FDUPES_DATABASE_DIRECTORY); + + db = hashdb_open(cachepath); + if (db == 0) + { + errormsg("could not open hash database at %s\n", cachepath); + free(cachehome); + free(cachepath); + exit(1); + } + + atexit(close_db_on_exit); + + free(cachehome); + free(cachepath); + } + else { + db = 0; + } + + if (db != 0) + { + hashdb_begintransaction(db); + + if (ISFLAG(flags, F_CLEARCACHE)) + hashdb_cleardirectories(db); + else if (ISFLAG(flags, F_PRUNECACHE)) { + hashdb_foreachdirectory(db, 0, delist_directory_if_missing); + hashdb_foreachhash(db, 0, delist_hash_if_orphaned); + } + } +#endif + + register_sigint_handler(); + if (ISFLAG(flags, F_RECURSEAFTER)) { - firstrecurse = nonoptafter("--recurse:", argc, oldargv, argv, optind); - - if (firstrecurse == argc) - firstrecurse = nonoptafter("-R", argc, oldargv, argv, optind); + firstrecurse = nonoptafter("--recurse:", argc, oldargv, argv, optind, &foundoption); - if (firstrecurse == argc) { + if (!foundoption) + firstrecurse = nonoptafter("-R", argc, oldargv, argv, optind, &foundoption); + + if (!foundoption) { errormsg("-R option must be isolated from other options\n"); exit(1); } + else if (firstrecurse == argc && optind != argc) + { + errormsg("-R option must be followed by at least one directory\n"); + exit(1); + } /* F_RECURSE is not set for directories before --recurse: */ for (x = optind; x < firstrecurse; x++) @@ -1484,10 +1814,15 @@ if (!ISFLAG(flags, F_HIDEPROGRESS)) fprintf(stderr, "\r%40s\r", " "); exit(0); } - + curfile = files; while (curfile) { + if (got_sigint) { + printf("\n"); + exit(0); + } + if (!checktree) registerfile(&checktree, curfile); else @@ -1514,7 +1849,7 @@ ordertype == ORDER_CTIME ? sort_pairs_by_ctime : sort_pairs_by_filename, loginfo ); } - else if (ISFLAG(flags, F_DEFERCONFIRMATION) || confirmmatch(file1, file2)) + else if (ISFLAG(flags, F_DEFERCONFIRMATION) || (ISFLAG(flags, F_SUMMARIZEMATCHES) && ISFLAG(flags, F_SUMMARIZEFASTER)) || confirmmatch(file1, file2)) registerpair(match, curfile, ordertype == ORDER_MTIME ? sort_pairs_by_mtime : ordertype == ORDER_CTIME ? sort_pairs_by_ctime : @@ -1541,6 +1876,11 @@ loginfo = 0; } +#ifndef NO_SQLITE + if (db != 0) + hashdb_committransaction(db); +#endif + if (ISFLAG(flags, F_DELETEFILES)) { if (ISFLAG(flags, F_NOPROMPT) || ISFLAG(flags, F_IMMEDIATE))
View file
_service:tar_scm:fdupes-2.2.1.tar.gz/fdupes.h -> _service:tar_scm:fdupes-2.3.1.tar.gz/fdupes.h
Changed
@@ -22,6 +22,7 @@ #ifndef FDUPES_H #define FDUPES_H +#include "config.h" #include <sys/stat.h> #include "md5/md5.h" @@ -34,6 +35,8 @@ ino_t inode; time_t mtime; time_t ctime; + long mtime_nsec; + long ctime_nsec; int hasdupes; /* true only if file is first on duplicate chain */ struct _file *duplicates; struct _file *next;
View file
_service:tar_scm:fdupes-2.2.1.tar.gz/flags.h -> _service:tar_scm:fdupes-2.3.1.tar.gz/flags.h
Changed
@@ -23,6 +23,12 @@ #define F_PLAINPROMPT 0x10000 #define F_SHOWTIME 0x20000 #define F_DEFERCONFIRMATION 0x40000 +#define F_CACHESIGNATURES 0x80000 +#define F_CLEARCACHE 0x100000 +#define F_PRUNECACHE 0x200000 +#define F_READONLYCACHE 0x400000 +#define F_VACUUMCACHE 0x800000 +#define F_SUMMARIZEFASTER 0x1000000 extern unsigned long flags;
View file
_service:tar_scm:fdupes-2.3.1.tar.gz/getrealpath.c
Added
@@ -0,0 +1,451 @@ +/* Copyright (c) 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 + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. */ + +#include "config.h" +#include "getrealpath.h" +#include "dir.h" +#include "sdirname.h" +#include "sbasename.h" +#include <string.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#define ISFLAG(a,b) ((a & b) == b) + +#ifndef GETREALPATH_MAXSYMLINKS +#define GETREALPATH_MAXSYMLINKS 40 +#endif + +#define DEFAULT_LINK_ALLOCATION_SIZE 16 + +/* read link contents into buffer allocated via malloc() */ +char *getlink(const char *path, struct stat *s) +{ + char *link; + char *buffer; + size_t allocated; + int linksize; + + if (s->st_size > 0) + allocated = s->st_size + 1; + else + allocated = DEFAULT_LINK_ALLOCATION_SIZE; + + buffer = malloc(allocated); + if (buffer == 0) + return 0; + + do + { + link = buffer; + + linksize = readlink(path, link, allocated); + if (linksize == -1) + { + free(link); + return 0; + } + + if (linksize < allocated) + { + linklinksize = '\0'; + return link; + } + + if (stat(path, s) != 0) + { + free(link); + return 0; + } + + if (s->st_size != 0) + allocated = s->st_size + 1; + else + allocated *= 2; + } while (buffer = realloc(link, allocated)); + + free(link); + + return 0; +} + +/* replace destfrom .. through with contents of src */ +int replacestring(char *dest, size_t from, size_t through, size_t max, const char *src) +{ + size_t srclength; + size_t destlength; + size_t moveto; + size_t newlength; + + destlength = strlen(dest); + + if (through >= destlength || from > through) + return 0; + + srclength = strlen(src); + + newlength = destlength + srclength - (through - from + 1); + if (newlength > max) + return 0; + + memmove(dest + from + srclength, dest + through + 1, destlength - through); + + memcpy(dest + from, src, srclength); + + return 1; +} + +/* print the resolved absolute file name for the specified path */ +char *getrealpath(const char *path, unsigned int options) +{ + char *scratch; + char *cwd; + char *link; + char *newmem; + char *dirname; + char *basename; + char save; + size_t tail; + size_t next; + size_t pathlength; + size_t linklength; + size_t cwdlength; + size_t allocated; + size_t links; + size_t x; + struct stat st; + struct stat st0; + int pathexists; + + /* run stat on unmodified path, for later use */ + pathexists = stat(path, &st0) == 0; + if (!pathexists && !ISFLAG(options, GETREALPATH_IGNORE_MISSING_BASENAME)) + return 0; + + /* optionally ignore the last component if it does not exist */ + if (ISFLAG(options, GETREALPATH_IGNORE_MISSING_BASENAME) && !pathexists) + { + dirname = malloc(strlen(path) + 1); + if (dirname == 0) + return 0; + + basename = malloc(strlen(path) + 1); + if (basename == 0) + return 0; + + sdirname(dirname, path); + sbasename(basename, path); + + if (stat(dirname, &st0) != 0) + return 0; + + link = getrealpath(dirname, options ^ GETREALPATH_IGNORE_MISSING_BASENAME); + if (link == 0) + { + free(basename); + free(dirname); + return 0; + } + + scratch = malloc(strlen(link) + strlen(basename) + 2); + if (scratch == 0) + { + free(basename); + free(dirname); + return 0; + } + + strcpy(scratch, link); + strcat(scratch, "/"); + strcat(scratch, basename); + + if (stat(dirname, &st) != 0) + { + free(link); + free(basename); + free(dirname); + free(scratch); + return 0; + } + + free(link); + free(basename); + free(dirname); + + if + ( + st.st_dev != st0.st_dev || + st.st_ino != st0.st_ino + ) + { + free(scratch); + return 0; + } + + return scratch; + } + + if (path0 == '/') + /* if path is an absolute path, copy its contents to scratch buffer */ + { + allocated = strlen(path) + 1; + + scratch = malloc(allocated); + if (scratch == 0) + return 0; + + memcpy(scratch, path, allocated); + } + else + /* if path is a relative path, combine cwd and path into scratch buffer */ + { + cwd = getworkingdirectory(); + if (cwd == 0) + return 0; + + pathlength = strlen(path); + cwdlength = strlen(cwd); + + allocated = pathlength + cwdlength + 2; + + scratch = malloc(allocated); + if (scratch == 0) + { + free(cwd); + return 0; + } + + memcpy(scratch, cwd, cwdlength); + + scratchcwdlength = '/'; + + memcpy(scratch + cwdlength + 1, path, pathlength + 1); + + free(cwd); + } + + tail = 0; + next = 0; + links = 0; + + while (scratchnext != '\0') + { + if (scratchnext == '/') + /* advance to start of filename */ + { + /* keep the first slash */ + scratchtail++ = scratchnext++; + + /* skip the rest */ + while (scratchnext == '/') + ++next; + + continue; + } + else if (scratchnext == '.') + /* handle filenames beginning with "." */ + { + switch (scratchnext + 1) + { + case '/': + /* collapse /./ down to / */ + do + ++next; + while (scratchnext == '/'); + + continue; + + case '\0': + /* truncate trailing /. down to / */ + ++next; + continue; + + case '.': + if (scratchnext + 2 == '/') + /* go up one directory from /../ */ + { + if (tail > 1) + tail -= 2; + else + tail = 0; + + while (scratchtail != '/') + --tail; + + if (tail == 0) + { + tail = 1; + ++next; + } + else + { + next += 2; + } + } + else if (scratchnext + 2 == '\0') + /* go up one directory from trailing /.. */ + { + if (tail > 1) + tail -= 2; + else + tail = 0; + + while (scratchtail != '/') + --tail; + + if (tail == 0) + { + tail = 1; + ++next; + } + else + { + next += 2; + } + } + else + /* process .. of regular filename beginning with .. */ + { + do + scratchtail++ = scratchnext++; + while (scratchnext == '.'); + } + + break; + + default: + /* process . of regular filename begining with . */ + scratchtail++ = scratchnext++; + break; + } + } + else + /* process regular filename characters */ + { + do + scratchtail++ = scratchnext++; + while (scratchnext != '\0' && scratchnext != '/'); + } + + save = scratchtail; + + scratchtail = '\0'; + + if (lstat(scratch, &st) != 0) + { + free(scratch); + return 0; + } + + if (S_ISLNK(st.st_mode)) + { + if (links++ > GETREALPATH_MAXSYMLINKS) + { + free(scratch); + return 0; + } + + link = getlink(scratch, &st); + if (link == 0) + { + free(scratch); + return 0; + } + + pathlength = strlen(scratch) + strlen(scratch + next); + + linklength = strlen(link); + + if (pathlength + linklength + 1 > allocated) + { + allocated += pathlength + linklength + 1; + + newmem = realloc(scratch, allocated); + if (newmem == 0) + { + free(scratch); + return 0; + } + + scratch = newmem; + } + + scratchtail = save; + + if (link0 == '/') + /* link represents an absolute path */ + { + memmove(scratch + tail, scratch + next, strlen(scratch + next) + 1); + + replacestring(scratch, 0, tail - 1, allocated, link); + + /* start over */ + tail = 0; + next = 0; + } + else + /* link represents a relative path */ + { + memmove(scratch + tail, scratch + next, strlen(scratch + next) + 1); + + x = tail; + + while (scratchx != '/') + --x; + + replacestring(scratch, x + 1, tail - 1, allocated, link); + + tail = x; + next = x; + } + + free(link); + } + else + { + scratchtail = save; + } + } + + /* terminate path */ + if (tail > 1 && scratchtail - 1 == '/') + scratchtail - 1 = '\0'; + else + scratchtail = '\0'; + + /* confirm that scratch and path both point to the same file */ + if (stat(scratch, &st) != 0) + { + free(scratch); + return 0; + } + + if + ( + st.st_dev != st0.st_dev || + st.st_ino != st0.st_ino + ) + { + free(scratch); + return 0; + } + + return scratch; +}
View file
_service:tar_scm:fdupes-2.3.1.tar.gz/getrealpath.h
Added
@@ -0,0 +1,27 @@ +/* Copyright (c) 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 + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. */ + +#ifndef GETREALPATH_H +#define GETREALPATH_H + +#define GETREALPATH_NONE 0b0 +#define GETREALPATH_IGNORE_MISSING_BASENAME 0b1 + +char *getrealpath(const char *path, unsigned int options); + +#endif \ No newline at end of file
View file
_service:tar_scm:fdupes-2.3.1.tar.gz/hashdb.c
Added
@@ -0,0 +1,752 @@ +/* FDUPES Copyright (c) 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 <sqlite3.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <assert.h> +#include "hashdb.h" +#include "getrealpath.h" +#include "sbasename.h" +#include "sdirname.h" +#include "errormsg.h" + +#define DATABASE_VERSION 1 + +#define HASH_FUNCTION_MD5 1 + +#define HASH_FUNCTION HASH_FUNCTION_MD5 +#define HASH_FUNCTION_OUTPUT_LENGTH 16 + +void md5copy(md5_byte_t *to, const md5_byte_t *from); + +#define PREPARE_STATEMENT(a, b) sqlite3_prepare_v2(db, a, -1, hashdb__newstatement(&b), 0) + +#define HASHDB_MAX_STATEMENTS 32 + +sqlite3_stmt **hashdb_statementsHASHDB_MAX_STATEMENTS; + +size_t hashdb_statements_top; + +sqlite3_stmt *query_begintransaction = 0; +sqlite3_stmt *query_committransaction = 0; +sqlite3_stmt *query_rollbacktransaction = 0; +sqlite3_stmt *query_vacuum = 0; +sqlite3_stmt *query_getdirectoryid = 0; +sqlite3_stmt *query_insertdirectory = 0; +sqlite3_stmt *query_deletedirectory = 0; +sqlite3_stmt *query_cleardirectories = 0; +sqlite3_stmt *query_foreachdirectory = 0; +sqlite3_stmt *query_foreachdirectorywithin = 0; +sqlite3_stmt *query_loadhash = 0; +sqlite3_stmt *query_savehash = 0; +sqlite3_stmt *query_deletehash = 0; +sqlite3_stmt *query_deletehashforpath = 0; +sqlite3_stmt *query_foreachhash = 0; +sqlite3_stmt *query_foreachhashwithin = 0; + +sqlite3_stmt **hashdb__newstatement(sqlite3_stmt **statement) +{ + assert(hashdb_statements_top + 1 <= HASHDB_MAX_STATEMENTS); + + hashdb_statementshashdb_statements_top++ = statement; + + return statement; +} + +int hashdb__createtables(sqlite3 *db) +{ + int result; + + hashdb_begintransaction(db); + + result = sqlite3_exec(db, + "CREATE TABLE IF NOT EXISTS directories (" + " id INTEGER PRIMARY KEY," + " name TEXT," + " full_path TEXT UNIQUE," + " parent INTEGER REFERENCES directories(id) ON DELETE CASCADE" + ")", + 0, 0, 0); + + if (result != SQLITE_OK) + return result; + + result = sqlite3_exec(db, + "CREATE TABLE IF NOT EXISTS hashes (" + " directory_id INTEGER REFERENCES directories(id) ON DELETE CASCADE," + " filename TEXT," + " inode BLOB," + " size INTEGER," + " ctime BLOB," + " mtime BLOB," + " ctime_nsec INTEGER," + " mtime_nsec INTEGER," + " partial_hash BLOB," + " partial_hash_bytes INTEGER," + " hash BLOB," + " hash_function INTEGER," + " PRIMARY KEY (directory_id, filename)" + ")", + 0, 0, 0); + + if (result != SQLITE_OK) { + hashdb_rollbacktransaction(db); + return result; + } + + hashdb_committransaction(db); + + return SQLITE_OK; +} + +int hashdb__preparestatements(sqlite3 *db) +{ + int result; + + /* standard SQL commands */ + result = PREPARE_STATEMENT("BEGIN", query_begintransaction); + if (result != SQLITE_OK) + return result; + + result = PREPARE_STATEMENT("COMMIT", query_committransaction); + if (result != SQLITE_OK) + return result; + + result = PREPARE_STATEMENT("ROLLBACK", query_rollbacktransaction); + if (result != SQLITE_OK) + return result; + + result = PREPARE_STATEMENT("VACUUM", query_vacuum); + if (result != SQLITE_OK) + return result; + + /* directory operations */ + result = PREPARE_STATEMENT("SELECT id FROM directories WHERE full_path = ?", query_getdirectoryid); + if (result != SQLITE_OK) + return result; + + result = PREPARE_STATEMENT("INSERT INTO directories (name, full_path, parent) VALUES (?, ?, ?)", query_insertdirectory); + if (result != SQLITE_OK) + return result; + + result = PREPARE_STATEMENT("DELETE FROM directories WHERE id = ?", query_deletedirectory); + if (result != SQLITE_OK) + return result; + + result = PREPARE_STATEMENT("DELETE FROM directories", query_cleardirectories); + if (result != SQLITE_OK) + return result; + + result = PREPARE_STATEMENT("SELECT id, name, full_path, parent FROM directories", query_foreachdirectory); + if (result != SQLITE_OK) + return result; + + result = PREPARE_STATEMENT("SELECT id, name, full_path, parent FROM directories WHERE parent = :parent", query_foreachdirectorywithin); + if (result != SQLITE_OK) + return result; + + /* hash operations */ + result = PREPARE_STATEMENT("SELECT hashes.partial_hash, hashes.hash FROM hashes INNER JOIN directories ON hashes.directory_id = directories.id WHERE directories.full_path = ? AND hashes.filename = ? AND hashes.inode = ? AND hashes.size = ? AND hashes.ctime = ? AND hashes.mtime = ? AND hashes.ctime_nsec = ? AND hashes.mtime_nsec = ? AND hashes.partial_hash_bytes = ? AND hashes.hash_function = ?", query_loadhash); + if (result != SQLITE_OK) + return result; + + result = PREPARE_STATEMENT("INSERT OR REPLACE INTO hashes (directory_id, filename, inode, size, ctime, mtime, ctime_nsec, mtime_nsec, partial_hash, partial_hash_bytes, hash, hash_function) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", query_savehash); + if (result != SQLITE_OK) + return result; + + result = PREPARE_STATEMENT("DELETE FROM hashes WHERE directory_id = ? AND filename = ?", query_deletehash); + if (result != SQLITE_OK) + return result; + + result = PREPARE_STATEMENT("DELETE FROM hashes WHERE filename = ? AND directory_id IN (SELECT id FROM directories WHERE full_path = ?)", query_deletehashforpath); + if (result != SQLITE_OK) + return result; + + result = PREPARE_STATEMENT("SELECT hashes.directory_id, hashes.filename, directories.full_path AS directory FROM hashes INNER JOIN directories ON hashes.directory_id = directories.id", query_foreachhash); + if (result != SQLITE_OK) + return result; + + result = PREPARE_STATEMENT("SELECT hashes.directory_id, hashes.filename, directories.full_path AS directory FROM hashes INNER JOIN directories ON hashes.directory_id = directories.id WHERE directories.id = :directory_id", query_foreachhashwithin); + if (result != SQLITE_OK) + return result; + + return SQLITE_OK; +} + +void hashdb__finalizestatements() +{ + size_t s; + + for (s = 0; s < hashdb_statements_top; ++s) { + sqlite3_finalize(*hashdb_statementss); + *hashdb_statementss = 0; + } + + hashdb_statements_top = 0; +} + +int hashdb__getdatabaseversion(sqlite3 *db, int *version) +{ + sqlite3_stmt *statement; + int result; + + result = sqlite3_prepare_v2(db, "PRAGMA user_version", -1, &statement, 0); + if (result != SQLITE_OK) + return result; + + result = sqlite3_step(statement); + if (result != SQLITE_ROW) + return result; + + *version = sqlite3_column_int(statement, 0); + + result = sqlite3_finalize(statement); + if (result != SQLITE_OK) + return result; + + return SQLITE_OK; +} + +int hashdb__setdatabaseversion(sqlite3 *db, int version) +{ + char query64; + int written; + + written = snprintf(query, sizeof(query), "PRAGMA user_version = %d", version); + if (written >= sizeof(query)) + return SQLITE_ERROR; + + return sqlite3_exec(db, query, 0, 0, 0); +} + +int hashdb__iscompatible(int major) +{ + return major <= DATABASE_VERSION; +} + +int hashdb__insertdirectory(sqlite3 *db, const char *name, const char *full_path, const sqlite3_int64 *parent) +{ + int result; + + sqlite3_bind_text(query_insertdirectory, 1, name, strlen(name), SQLITE_TRANSIENT); + sqlite3_bind_text(query_insertdirectory, 2, full_path, strlen(full_path), SQLITE_TRANSIENT); + + if (parent != 0) + sqlite3_bind_int64(query_insertdirectory, 3, *parent); + else + sqlite3_bind_null(query_insertdirectory, 3); + + result = sqlite3_step(query_insertdirectory); + + sqlite3_reset(query_insertdirectory); + + return result == SQLITE_DONE; +} + +int hashdb__enable_foreign_keys(sqlite3 *db) +{ + return sqlite3_exec(db, "PRAGMA foreign_keys = ON", 0, 0, 0) == SQLITE_OK; +} + +int hashdb__foreign_keys_enabled(sqlite3 *db) +{ + sqlite3_stmt *statement; + int value; + int result; + + result = sqlite3_prepare_v2(db, "PRAGMA foreign_keys", -1, &statement, 0); + if (result != SQLITE_OK) + return 0; + + result = sqlite3_step(statement); + if (result != SQLITE_ROW) + return 0; + + value = sqlite3_column_int(statement, 0); + + result = sqlite3_finalize(statement); + if (result != SQLITE_OK) + return 0; + + return value; +} + +int hashdb__enable_write_ahead(sqlite3 *db) +{ + return sqlite3_exec(db, "PRAGMA journal_mode = WAL", 0, 0, 0) == SQLITE_OK; +} + +sqlite3 *hashdb_open(const char *path) +{ + sqlite3 *db; + int result; + int version; + + result = sqlite3_open_v2(path, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0); + if (result != SQLITE_OK) + return 0; + + if (!hashdb__enable_write_ahead(db)) { + sqlite3_close_v2(db); + return 0; + } + + if (!hashdb__enable_foreign_keys(db)) { + sqlite3_close_v2(db); + return 0; + } + + if (!hashdb__foreign_keys_enabled(db)) { + sqlite3_close_v2(db); + return 0; + } + + result = hashdb__getdatabaseversion(db, &version); + if (result != SQLITE_OK || !hashdb__iscompatible(version)) { + sqlite3_close_v2(db); + return 0; + } + + if (version == 0) /* this is a new database */ { + result = hashdb__createtables(db); + if (result != SQLITE_OK) { + sqlite3_close_v2(db); + return 0; + } + + result = hashdb__setdatabaseversion(db, DATABASE_VERSION); + if (result != SQLITE_OK) { + sqlite3_close_v2(db); + return 0; + } + } + + if (hashdb__preparestatements(db) != SQLITE_OK) { + sqlite3_close_v2(db); + return 0; + } + + return db; +} + +int hashdb_close(sqlite3 *db) +{ + hashdb__finalizestatements(); + + return sqlite3_close_v2(db); +} + +int hashdb_begintransaction(sqlite3 *db) +{ + int result; + + result = sqlite3_step(query_begintransaction); + + sqlite3_reset(query_begintransaction); + + return result == SQLITE_DONE; +} + +int hashdb_committransaction(sqlite3 *db) +{ + int result; + + result = sqlite3_step(query_committransaction); + + sqlite3_reset(query_committransaction); + + return result == SQLITE_DONE; +} + +int hashdb_rollbacktransaction(sqlite3 *db) +{ + int result; + + result = sqlite3_step(query_rollbacktransaction); + + sqlite3_reset(query_rollbacktransaction); + + return result == SQLITE_DONE; +} + +int hashdb_vacuum(sqlite3 *db) +{ + int result; + + result = sqlite3_step(query_vacuum); + + sqlite3_reset(query_vacuum); + + return result == SQLITE_DONE; +} + +int hashdb_getdirectoryid(sqlite3 *db, const char *path, sqlite_int64 *directory_id) +{ + int result; + + sqlite3_bind_text(query_getdirectoryid, 1, path, strlen(path), SQLITE_TRANSIENT); + + result = sqlite3_step(query_getdirectoryid); + + if (result == SQLITE_ROW) + *directory_id = sqlite3_column_int64(query_getdirectoryid, 0); + + sqlite3_reset(query_getdirectoryid); + + return result == SQLITE_ROW; +} + +int hashdb_savedirectory(sqlite3 *db, const char *path) +{ + int result; + char *dir; + char *base; + sqlite3_int64 parentid; + + dir = sdirname(0, path); + if (!dir) + return 0; + + base = sbasename(0, path); + if (!base) + { + free(dir); + return 0; + } + + if (!hashdb_getdirectoryid(db, dir, &parentid)) + { + if (strcmp(path, "/") == 0) + { + result = hashdb__insertdirectory(db, "/", "/", 0); + + free(base); + free(dir); + + return result; + } + + if (!hashdb_savedirectory(db, dir)) + { + free(base); + free(dir); + + return 0; + } + + parentid = sqlite3_last_insert_rowid(db); + } + + result = hashdb__insertdirectory(db, base, path, &parentid); + + free(base); + free(dir); + + return result; +} + +int hashdb_deletedirectory(sqlite3 *db, sqlite3_int64 id) +{ + int result; + + sqlite3_bind_int64(query_deletedirectory, 1, id); + + result = sqlite3_step(query_deletedirectory); + + sqlite3_reset(query_deletedirectory); + + return result == SQLITE_DONE; +} + +int hashdb_cleardirectories(sqlite3 *db) +{ + int result; + + result = sqlite3_step(query_cleardirectories); + + sqlite3_reset(query_cleardirectories); + + return result == SQLITE_DONE; +} + +int hashdb_foreachdirectory(sqlite3 *db, const sqlite3_int64 *parent, int (*callback)(const sqlite3_int64, const char*, const char*, const sqlite3_int64)) +{ + int result; + sqlite3_stmt *query; + + if (parent != 0) { + query = query_foreachdirectorywithin; + sqlite3_bind_int64(query, 1, *parent); + } else { + query = query_foreachdirectory; + sqlite3_bind_null(query, 1); + } + + result = sqlite3_step(query); + + while (result == SQLITE_ROW) + { + result = callback( + sqlite3_column_int64(query, 0), + sqlite3_column_text(query, 1), + sqlite3_column_text(query, 2), + sqlite3_column_int64(query, 3) + ); + + if (result == 0) { + sqlite3_reset(query); + return 1; + } + + result = sqlite3_step(query); + } + + sqlite3_reset(query); + + return result == SQLITE_DONE; +} + +int hashdb_loadhash(sqlite3 *db, const file_t *entry, md5_byte_t **partialhash, md5_byte_t **fullhash) +{ + int result; + int hashsize; + char *realpath; + char *name; + + realpath = getrealpath(entry->d_name, 0); + if (realpath == 0) + return 0; + + name = malloc(strlen(realpath) + 1); + if (name == 0) + { + free(realpath); + return 0; + } + + sdirname(name, realpath); + sqlite3_bind_text(query_loadhash, 1, name, strlen(name), SQLITE_TRANSIENT); + + sbasename(name, realpath); + sqlite3_bind_text(query_loadhash, 2, name, strlen(name), SQLITE_TRANSIENT); + + sqlite3_bind_blob(query_loadhash, 3, &entry->inode, sizeof(entry->inode), SQLITE_TRANSIENT); + sqlite3_bind_int64(query_loadhash, 4, entry->size); + sqlite3_bind_blob(query_loadhash, 5, &entry->ctime, sizeof(entry->ctime), SQLITE_TRANSIENT); + sqlite3_bind_blob(query_loadhash, 6, &entry->mtime, sizeof(entry->mtime), SQLITE_TRANSIENT); + sqlite3_bind_int64(query_loadhash, 7, entry->ctime_nsec); + sqlite3_bind_int64(query_loadhash, 8, entry->mtime_nsec); + sqlite3_bind_int64(query_loadhash, 9, PARTIAL_MD5_SIZE); + sqlite3_bind_int(query_loadhash, 10, HASH_FUNCTION); + + result = sqlite3_step(query_loadhash); + + free(name); + free(realpath); + + if (result != SQLITE_ROW) + { + sqlite3_reset(query_loadhash); + return 0; + } + + hashsize = sqlite3_column_bytes(query_loadhash, 0); + + if (hashsize == HASH_FUNCTION_OUTPUT_LENGTH * sizeof(md5_byte_t)) + { + *partialhash = (md5_byte_t*) malloc(HASH_FUNCTION_OUTPUT_LENGTH * sizeof(md5_byte_t)); + if (*partialhash == NULL) { + errormsg("out of memory\n"); + exit(1); + } + + md5copy(*partialhash, sqlite3_column_blob(query_loadhash, 0)); + } + else + { + *partialhash = 0; + } + + hashsize = sqlite3_column_bytes(query_loadhash, 1); + + if (hashsize == HASH_FUNCTION_OUTPUT_LENGTH * sizeof(md5_byte_t)) + { + *fullhash = (md5_byte_t*) malloc(HASH_FUNCTION_OUTPUT_LENGTH * sizeof(md5_byte_t)); + if (*fullhash == NULL) { + errormsg("out of memory\n"); + exit(1); + } + + md5copy(*fullhash, sqlite3_column_blob(query_loadhash, 1)); + } + else + { + *fullhash = 0; + } + + sqlite3_reset(query_loadhash); + + return *partialhash || *fullhash; +} + +int hashdb_savehash(sqlite3 *db, const file_t *entry, md5_byte_t *partialhash, md5_byte_t *fullhash) +{ + int result; + char *realpath; + char *name; + sqlite3_int64 directoryid; + + realpath = getrealpath(entry->d_name, 0); + if (realpath == 0) + return 0; + + name = malloc(strlen(realpath) + 1); + if (name == 0) + { + free(realpath); + return 0; + } + + sdirname(name, realpath); + + if (!hashdb_getdirectoryid(db, name, &directoryid)) + { + if (!hashdb_savedirectory(db, name)) + { + free(name); + free(realpath); + return 0; + } + + directoryid = sqlite3_last_insert_rowid(db); + } + + sbasename(name, realpath); + + sqlite3_bind_int64(query_savehash, 1, directoryid); + sqlite3_bind_text(query_savehash, 2, name, strlen(name), SQLITE_TRANSIENT); + sqlite3_bind_blob(query_savehash, 3, &entry->inode, sizeof(entry->inode), SQLITE_TRANSIENT); + sqlite3_bind_int64(query_savehash, 4, entry->size); + sqlite3_bind_blob(query_savehash, 5, &entry->ctime, sizeof(entry->ctime), SQLITE_TRANSIENT); + sqlite3_bind_blob(query_savehash, 6, &entry->mtime, sizeof(entry->mtime), SQLITE_TRANSIENT); + sqlite3_bind_int64(query_savehash, 7, entry->ctime_nsec); + sqlite3_bind_int64(query_savehash, 8, entry->mtime_nsec); + + if (partialhash) + sqlite3_bind_blob(query_savehash, 9, partialhash, HASH_FUNCTION_OUTPUT_LENGTH * sizeof(md5_byte_t), SQLITE_TRANSIENT); + else + sqlite3_bind_null(query_savehash, 9); + + sqlite3_bind_int64(query_savehash, 10, PARTIAL_MD5_SIZE); + + if (fullhash) + sqlite3_bind_blob(query_savehash, 11, fullhash, HASH_FUNCTION_OUTPUT_LENGTH * sizeof(md5_byte_t), SQLITE_TRANSIENT); + else + sqlite3_bind_null(query_savehash, 11); + + sqlite3_bind_int(query_savehash, 12, HASH_FUNCTION); + + result = sqlite3_step(query_savehash); + + free(name); + free(realpath); + + sqlite3_reset(query_savehash); + + return result == SQLITE_DONE; +} + +int hashdb_foreachhash(sqlite3 *db, sqlite3_int64 *directoryid, int (*callback)(const sqlite3_int64, const char*, const char*)) +{ + int result; + sqlite3_stmt *query; + + if (directoryid != 0) { + query = query_foreachhashwithin; + sqlite3_bind_int64(query, 1, *directoryid); + } else { + query = query_foreachhash; + sqlite3_bind_null(query, 1); + } + + result = sqlite3_step(query); + while (result == SQLITE_ROW) + { + result = callback( + sqlite3_column_int64(query, 0), + sqlite3_column_text(query, 1), + sqlite3_column_text(query, 2) + ); + + if (result == 0) { + sqlite3_reset(query); + return 1; + } + + result = sqlite3_step(query); + } + + sqlite3_reset(query); + + return result == SQLITE_DONE; +} + +int hashdb_deletehash(sqlite3 *db, sqlite3_int64 directoryid, const char *filename) +{ + int result; + + sqlite3_bind_int64(query_deletehash, 1, directoryid); + sqlite3_bind_text(query_deletehash, 2, filename, strlen(filename), SQLITE_TRANSIENT); + + result = sqlite3_step(query_deletehash); + + sqlite3_reset(query_deletehash); + + return result == SQLITE_DONE; +} + +int hashdb_deletehashforpath(sqlite3 *db, const char *path) +{ + int result; + char *name; + sqlite3_int64 pathid; + + name = malloc(strlen(path) + 1); + if (name == 0) + return 0; + + sbasename(name, path); + sqlite3_bind_text(query_deletehashforpath, 1, name, strlen(name), SQLITE_TRANSIENT); + + sdirname(name, path); + sqlite3_bind_text(query_deletehashforpath, 2, name, strlen(name), SQLITE_TRANSIENT); + + free(name); + + result = sqlite3_step(query_deletehashforpath); + + sqlite3_reset(query_deletehashforpath); + + return result == SQLITE_DONE; +} \ No newline at end of file
View file
_service:tar_scm:fdupes-2.3.1.tar.gz/hashdb.h
Added
@@ -0,0 +1,45 @@ +/* FDUPES Copyright (c) 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 HASHDB_H +#define HASHDB_H + +#include "fdupes.h" +#include <sqlite3.h> + +sqlite3 *hashdb_open(const char *path); +int hashdb_close(sqlite3 *db); +int hashdb_begintransaction(sqlite3 *db); +int hashdb_committransaction(sqlite3 *db); +int hashdb_rollbacktransaction(sqlite3 *db); +int hashdb_vacuum(sqlite3 *db); +int hashdb_getdirectoryid(sqlite3 *db, const char *path, sqlite3_int64 *directoryid); +int hashdb_savedirectory(sqlite3 *db, const char *path); +int hashdb_deletedirectory(sqlite3 *db, sqlite3_int64 id); +int hashdb_cleardirectories(sqlite3 *db); +int hashdb_foreachdirectory(sqlite3 *db, const sqlite3_int64 *parentid, int (*callback)(const sqlite3_int64, const char*, const char*, const sqlite3_int64)); +int hashdb_loadhash(sqlite3 *db, const file_t *entry, md5_byte_t **partialhash, md5_byte_t **fullhash); +int hashdb_savehash(sqlite3 *db, const file_t *entry, md5_byte_t *partialhash, md5_byte_t *fullhash); +int hashdb_foreachhash(sqlite3 *db, sqlite3_int64 *directoryid, int (*callback)(const sqlite3_int64, const char*, const char*)); +int hashdb_deletehash(sqlite3 *db, sqlite3_int64 directoryid, const char *filename); +int hashdb_deletehashforpath(sqlite3 *db, const char *path); + +#endif \ No newline at end of file
View file
_service:tar_scm:fdupes-2.2.1.tar.gz/ncurses-commands.c -> _service:tar_scm:fdupes-2.3.1.tar.gz/ncurses-commands.c
Changed
@@ -30,9 +30,17 @@ #include "mbstowcs_escape_invalid.h" #include "log.h" #include "removeifnotchanged.h" +#ifndef NO_SQLITE + #include "hashdb.h" + #include "getrealpath.h" +#endif #include <wchar.h> #include <pcre2.h> +#ifndef NO_SQLITE +extern sqlite3 *db; +#endif + void set_file_action(struct groupfile *file, int new_action, size_t *deletion_tally); struct command_map command_list = { @@ -706,6 +714,7 @@ int ismatch; wchar_t *statuscopy; struct groupfile *firstnotdeleted; + char *deletepath; if (logfile != 0) loginfo = log_open(logfile, 0); @@ -749,6 +758,10 @@ if (loginfo) log_begin_set(loginfo); +#ifndef NO_SQLITE + hashdb_begintransaction(db); +#endif + /* delete files marked for deletion unless no files left undeleted */ if (deletecount < groupsg.filecount) { @@ -781,6 +794,20 @@ ismatch = 1; } +#ifndef NO_SQLITE + if (ismatch && db) + { + deletepath = getrealpath(groupsg.filesf.file->d_name, GETREALPATH_IGNORE_MISSING_BASENAME); + if (deletepath != 0) + { + if (!ISFLAG(flags, F_READONLYCACHE)) + hashdb_deletehashforpath(db, deletepath); + + free(deletepath); + } + } +#endif + if (ismatch && removeifnotchanged(groupsg.filesf.file, 0) == 0) { set_file_action(&groupsg.filesf, FILEACTION_DELIST, deletiontally); @@ -813,6 +840,10 @@ deletecount = 0; } +#ifndef NO_SQLITE + hashdb_committransaction(db); +#endif + if (loginfo) log_end_set(loginfo); @@ -941,5 +972,5 @@ *topline = 0; } - cmd_clear_all_selections(groups, *totalgroups, commandarguments, 0); -} \ No newline at end of file + return cmd_clear_all_selections(groups, *totalgroups, commandarguments, 0); +}
View file
_service:tar_scm:fdupes-2.2.1.tar.gz/ncurses-getcommand.c -> _service:tar_scm:fdupes-2.3.1.tar.gz/ncurses-getcommand.c
Changed
@@ -22,6 +22,7 @@ #include "config.h" #include <stdlib.h> #include <signal.h> +#include <wctype.h> #include "ncurses-getcommand.h" #define KEY_ESCAPE 27
View file
_service:tar_scm:fdupes-2.2.1.tar.gz/ncurses-interface.c -> _service:tar_scm:fdupes-2.3.1.tar.gz/ncurses-interface.c
Changed
@@ -23,6 +23,7 @@ #include <stdlib.h> #include <string.h> #include <wchar.h> +#include <wctype.h> #ifdef HAVE_NCURSESW_CURSES_H #include <ncursesw/curses.h> #else @@ -462,8 +463,6 @@ exit(1); } - register_sigint_handler(); - curfile = files; while (curfile) {
View file
_service:tar_scm:fdupes-2.2.1.tar.gz/removeifnotchanged.c -> _service:tar_scm:fdupes-2.3.1.tar.gz/removeifnotchanged.c
Changed
@@ -1,3 +1,25 @@ +/* FDUPES Copyright (c) 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 "removeifnotchanged.h" #include <errno.h> #include <string.h> @@ -17,6 +39,10 @@ file->inode != st.st_ino || file->ctime != st.st_ctime || file->mtime != st.st_mtime || +#ifdef HAVE_NSEC_TIMES + file->ctime_nsec != st.st_ctim.tv_nsec || + file->mtime_nsec != st.st_mtim.tv_nsec || +#endif file->size != st.st_size) { if (errorstring != 0)
View file
_service:tar_scm:fdupes-2.2.1.tar.gz/removeifnotchanged.h -> _service:tar_scm:fdupes-2.3.1.tar.gz/removeifnotchanged.h
Changed
@@ -1,3 +1,24 @@ +/* FDUPES Copyright (c) 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 REMOVEIFNOTCHANGED_H #define REMOVEIFNOTCHANGED_H
View file
_service:tar_scm:fdupes-2.3.1.tar.gz/sbasename.c
Added
@@ -0,0 +1,26 @@ +/* The contents of this file are in the public domain. +*/ +#include "sbasename.h" +#include <string.h> +#include <libgen.h> +#include <stdlib.h> + +char *sbasename(char *str, const char *path) +{ + char *name; + + if (str == 0) + { + str = malloc(strlen(path) + 1); + if (str == 0) + return 0; + } + + strcpy(str, path); + + name = basename(str); + + memmove(str, name, strlen(name) + 1); + + return str; +} \ No newline at end of file
View file
_service:tar_scm:fdupes-2.3.1.tar.gz/sbasename.h
Added
@@ -0,0 +1,10 @@ +/* The contents of this file are in the public domain. +*/ +#ifndef SNBASENAME_H +#define SNBASENAME_H + +#include <stddef.h> + +char *sbasename(char *str, const char *path); + +#endif \ No newline at end of file
View file
_service:tar_scm:fdupes-2.3.1.tar.gz/sdirname.c
Added
@@ -0,0 +1,26 @@ +/* The contents of this file are in the public domain. +*/ +#include "sdirname.h" +#include <string.h> +#include <libgen.h> +#include <stdlib.h> + +char *sdirname(char *str, const char *path) +{ + char *name; + + if (str == 0) + { + str = malloc(strlen(path) + 1); + if (str == 0) + return 0; + } + + strcpy(str, path); + + name = dirname(str); + + memmove(str, name, strlen(name) + 1); + + return str; +} \ No newline at end of file
View file
_service:tar_scm:fdupes-2.3.1.tar.gz/sdirname.h
Added
@@ -0,0 +1,10 @@ +/* The contents of this file are in the public domain. +*/ +#ifndef SNDIRNAME_H +#define SNDIRNAME_H + +#include <stddef.h> + +char *sdirname(char *str, const char *path); + +#endif \ No newline at end of file
View file
_service:tar_scm:fdupes-2.3.1.tar.gz/xdgbase.c
Added
@@ -0,0 +1,69 @@ +/* The contents of this file are in the public domain. +*/ +#include "config.h" +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <pwd.h> +#include <string.h> + +#define XDG_CACHE_HOME_BASENAME ".cache" +#define XDG_CACHE_HOME_PERMISSIONS 0700 + +char *getcachehome(int createdefault) +{ + char *cachedir; + char *homedir; + char *result; + size_t pathlength; + size_t defaultdirlength; + struct passwd *pw; + struct stat st; + + cachedir = getenv("XDG_CACHE_HOME"); + if (cachedir != 0) + { + pathlength = strlen(cachedir); + + result = malloc(pathlength + 1); + if (result == 0) + return 0; + + strcpy(result, cachedir); + } + else + { + homedir = getenv("HOME"); + if (homedir == 0) + { + pw = getpwuid(getuid()); + if (pw == 0) + return 0; + + homedir = pw->pw_dir; + } + + if (homedir == 0) + return 0; + + pathlength = strlen(homedir); + + defaultdirlength = strlen(XDG_CACHE_HOME_BASENAME); + + result = malloc(pathlength + defaultdirlength + 2); + if (result == 0) + return 0; + + memmove(result, homedir, pathlength); + memmove(result + pathlength + 1, XDG_CACHE_HOME_BASENAME, defaultdirlength); + + resultpathlength = '/'; + resultpathlength + defaultdirlength + 1 = '\0'; + + if (createdefault && stat(result, &st) != 0) + mkdir(result, XDG_CACHE_HOME_PERMISSIONS); + } + + return result; +} \ No newline at end of file
View file
_service:tar_scm:fdupes-2.3.1.tar.gz/xdgbase.h
Added
@@ -0,0 +1,8 @@ +/* The contents of this file are in the public domain. +*/ +#ifndef GETCONFIGDIR_H +#define GETCONFIGDIR_H + +char *getcachehome(int createdefault); + +#endif \ No newline at end of file
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.
浙ICP备2022010568号-2