Import Upstream version 1.8

parent 525656d7
......@@ -17,9 +17,9 @@
#
# Normal output and testing dirs
#
/?dupes
/?dupes-jody
/?dupes*.exe
/jdupes
/jdupes*.exe
/*.pkg.tar.xz
#
# Backups / patches
......
jdupes 1.8
- All files are now licensed under The MIT License exclusively
- Fixed a serious memory alloc bug; upgrading is *strongly* recommended
- Several huge improvements to progress indicators
- Fix some error message display problems and add more error checking
- Fixes for several potential crashes and buffer overflows
- Indicate no duplicates were found if printing matches and none exist
- On Linux, jdupes now auto-tunes I/O size based on CPU L1 D-cache size
- The -v switch now also shows info about bitness in the version string
jdupes 1.7
- Incompatible change: zero-length files no longer duplicates by default
......
......@@ -6,8 +6,8 @@ make
su root
make install
This will install the program in /usr/local/bin. You may change this
to a different location by editing the Makefile. Please refer to the
This will install the program in /usr/bin. You may change this to a
different location by editing the Makefile. Please refer to the
Makefile for an explanation of compile-time options. If you're having
trouble compiling, please take a look at the Makefile.
......
The MIT License (MIT)
Copyright (c) 2015-2016 Jody Lee Bruchon and contributors
Copyright (C) 2015-2017 Jody Lee Bruchon and contributors
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
......
......@@ -20,6 +20,10 @@ PREFIX = /usr
# This can be enabled at build time: 'make LOW_MEMORY=1'
#LOW_MEMORY=1
# Uncomment this to build in hardened mode.
# This can be enabled at build time: 'make HARDEN=1'
#HARDEN=1
#####################################################################
# Developer Configuration Section #
#####################################################################
......@@ -63,6 +67,10 @@ endif
ifdef DEBUG
COMPILER_OPTIONS += -DDEBUG
endif
ifdef HARDEN
COMPILER_OPTIONS += -Wformat -Wformat-security -D_FORTIFY_SOURCE=2 -fstack-protector-strong -fPIE -fpie -Wl,-z,relro -Wl,-z,now
endif
# MinGW needs this for printf() conversions to work
ifeq ($(OS), Windows_NT)
......@@ -83,6 +91,7 @@ endif
# New BTRFS support option
ifdef ENABLE_BTRFS
COMPILER_OPTIONS += -DENABLE_BTRFS
OBJECT_FILES += act_dedupefiles.o
endif
# Low memory mode
ifdef LOW_MEMORY
......@@ -99,7 +108,8 @@ INSTALL_DATA = $(INSTALL) -c -m 0644
#ADDITIONAL_OBJECTS += getopt.o
OBJECT_FILES += jdupes.o jody_hash.o jody_paths.o jody_sort.o jody_win_unicode.o string_malloc.o
OBJECT_FILES += act_deletefiles.o act_dedupefiles.o act_linkfiles.o act_printmatches.o act_summarize.o
OBJECT_FILES += jody_cacheinfo.o
OBJECT_FILES += act_deletefiles.o act_linkfiles.o act_printmatches.o act_summarize.o
OBJECT_FILES += $(ADDITIONAL_OBJECTS)
all: jdupes
......@@ -117,3 +127,9 @@ install: jdupes installdirs
clean:
$(RM) $(OBJECT_FILES) $(PROGRAM_NAME) jdupes.exe *~ *.gcno *.gcda *.gcov
distclean: clean
$(RM) *.pkg.tar.xz
package:
+./chroot_build.sh
......@@ -299,10 +299,9 @@ Please DO NOT contact Adrian Lopez about issues with jdupes.
Legal Information and Software License
--------------------------------------------------------------------------
jdupes is Copyright (C) 2015-2016 by Jody Bruchon <jody@jodybruchon.com>
Derived from the original 'fdupes' (C) 1999-2016 by Adrian Lopez
Includes jody_hash, jody_paths, jody_sort, and string_malloc, all of
which are (C) 2015-2016 Jody Bruchon
jdupes is Copyright (C) 2015-2017 by Jody Bruchon <jody@jodybruchon.com>
Derived from the original 'fdupes' (C) 1999-2017 by Adrian Lopez
Includes other code libraries which are (C) 2015-2017 by Jody Bruchon
The MIT License
......
- A bug with -S shows wrong results.
- A bug causes the following behavior:
$ jdupes --symlinks testdir
testdir/with spaces b
testdir/with spaces a
testdir/zero_b
testdir/zero_a
testdir/symlink_two
testdir/twice_one
$ cp testdir/two testdir/two_again
$ jdupes --symlinks testdir
testdir/two_again
testdir/two
testdir/twice_one
testdir/symlink_two
testdir/with spaces b
testdir/with spaces a
testdir/zero_b
testdir/zero_a
** This is not the desired behavior. Likewise:
$ jdupes testdir
testdir/with spaces b
testdir/with spaces a
testdir/zero_b
testdir/zero_a
testdir/twice_one
testdir/two
$ jdupes --symlinks testdir
testdir/with spaces b
testdir/with spaces a
testdir/zero_b
testdir/zero_a
testdir/symlink_two
testdir/twice_one
- Option -R should not have to be separated from the rest,
such that "jdupes -dR testdir", "jdupes -d -R testdir",
"jdupes -Rd testdir", etc., all yield the same results.
......@@ -61,19 +12,6 @@
explicit loading in future runs to speed up repeated calls to
the program.
- Add an option to exclude files in the same directory from being
matched. This should be extensible so that various exclusion
criteria can be added later.
- Also add code to make directory ordering of pair matching more
stable; for instance, if dirs 'foo' and 'bar' are specified,
always put 'foo' at the start of the dupe set (which makes it
take precedence over 'bar' in this example.)
- The way that duplicates are sorted does not seem to work properly. The
sorting needs to be separated into its own function that sorts fully.
As it stands, sorting is only correct on specific match pairs and is
sometimes overridden by other match pairs to mess up the sort.
- The --xsize option can be improved. Instead of simply specifying an
exclusion size min/max, the option should offer multiple ways to
specify allowed file sizes. Examples:
......
/* BTRFS deduplication of file blocks */
/* BTRFS deduplication of file blocks
* This file is part of jdupes; see jdupes.c for license information */
#include "jdupes.h"
......@@ -9,9 +10,8 @@
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/btrfs.h>
#include <sys/ioctl.h>
#include "act_dedupefiles.h"
/* Message to append to BTRFS warnings based on write permissions */
......
/* jdupes action for BTRFS block-level deduplication */
/* jdupes action for BTRFS block-level deduplication
* This file is part of jdupes; see jdupes.c for license information */
#ifndef ACT_DEDUPEFILES_H
#define ACT_DEDUPEFILES_H
......
/* Delete duplicate files automatically or interactively */
/* Delete duplicate files automatically or interactively
* This file is part of jdupes; see jdupes.c for license information */
#include <stdio.h>
#include <stdlib.h>
......
/* jdupes action for deleting duplicate files */
/* jdupes action for deleting duplicate files
* This file is part of jdupes; see jdupes.c for license information */
#ifndef ACT_DELETEFILES_H
#define ACT_DELETEFILES_H
......
/* Hard link or symlink files */
/* Hard link or symlink files
* This file is part of jdupes; see jdupes.c for license information */
#include "jdupes.h"
......
/* jdupes action for hard and soft file linking */
/* jdupes action for hard and soft file linking
* This file is part of jdupes; see jdupes.c for license information */
#ifndef ACT_LINKFILES_H
#define ACT_LINKFILES_H
......
/* Print matched file sets
* This file is part of jdupes; see jdupes.c for license information */
#include <stdio.h>
#include "jdupes.h"
#include "jody_win_unicode.h"
......@@ -6,9 +9,11 @@
extern void printmatches(file_t * restrict files)
{
file_t * restrict tmpfile;
int printed = 0;
while (files != NULL) {
if (ISFLAG(files->flags, F_HAS_DUPES)) {
printed = 1;
if (!ISFLAG(flags, F_OMITFIRST)) {
if (ISFLAG(flags, F_SHOWSIZE)) printf("%jd byte%c each:\n", (intmax_t)files->size,
(files->size != 1) ? 's' : ' ');
......@@ -25,5 +30,8 @@ extern void printmatches(file_t * restrict files)
files = files->next;
}
if (printed == 0) fprintf(stderr, "No duplicates found.\n");
return;
}
/* jdupes action for printing matched file sets to stdout */
/* jdupes action for printing matched file sets to stdout
* This file is part of jdupes; see jdupes.c for license information */
#ifndef ACT_PRINTMATCHES_H
#define ACT_PRINTMATCHES_H
......
/* Print summary of match statistics to stdout */
/* Print summary of match statistics to stdout
* This file is part of jdupes; see jdupes.c for license information */
#include <stdio.h>
#include "jdupes.h"
......
/* jdupes action for printing a summary of match stats to stdout */
/* jdupes action for printing a summary of match stats to stdout
* This file is part of jdupes; see jdupes.c for license information */
#ifndef ACT_SUMMARIZE_H
#define ACT_SUMMARIZE_H
......
#!/bin/sh
# Jody's generic chroot build script
# Version 1.0
ARCHES="i386 x86-64 uclibc-i386 uclibc-x86-64"
test -z "$NAME" && NAME="$(basename "$(pwd)")"
test -e "version.h" && VER="$(grep '#define VER ' version.h | tr -d \\\" | cut -d' ' -f3)"
test -z "$VER" && VER=0
export NAME
export VER
export CHROOT_BASE=/chroots
export WD="$(pwd)"
export PKG="pkg"
echo "chroot builder: building '$NAME' version '$VER'"
trap clean_exit INT QUIT ABRT HUP
clean_exit () {
umount $CHROOT/proc $CHROOT/sys $CHROOT/tmp $CHROOT/dev $CHROOT/usr/src $CHROOT/home
}
do_build () {
test -z "$WD" && echo "WD not set, aborting" && exit 1
test -z "$PKG" && echo "PKG not set, aborting" && exit 1
make clean
if ! make -j$JOBS all
then echo "Build failed"; exit 1
else
echo "WD/PKG: $WD/$PKG"
test -d $WD/$PKG && rm -rf $WD/$PKG
mkdir $WD/$PKG
make DESTDIR=$WD/$PKG install && \
tar -C pkg -c usr | xz -e > ${NAME}_$VER-$ARCH.pkg.tar.xz
echo "Built ${NAME}_$VER-$ARCH.pkg.tar.xz"
fi
}
if [ "$(id -u)" != "0" ]
then echo "You must be root to auto-build chroot packages."
exit 1
fi
if [ "$DO_CHROOT_BUILD" = "1" ]
then
test -z "$1" && echo "No arch specified" && exit 1
test ! -d "$1" && echo "Not a directory: $1" && exit 1
cd $1
export WD="$1"
do_build
echo "finished: $1"
exit
else
echo baz
export DO_CHROOT_BUILD=1
for ARCH in $ARCHES
do
export ARCH
export CHROOT="$CHROOT_BASE/$ARCH"
test ! -d $CHROOT && echo "$CHROOT not present, not building $ARCH package." && continue
echo "Performing package build for $CHROOT"
test ! -x $CHROOT/bin/sh && echo "$CHROOT does not seem to be a chroot; aborting." && exit 1
mount --bind /dev $CHROOT/dev || clean_exit
mount --bind /usr/src $CHROOT/usr/src || clean_exit
mount --bind /home $CHROOT/home || clean_exit
mount -t proc proc $CHROOT/proc || clean_exit
mount -t sysfs sysfs $CHROOT/sys || clean_exit
mount -t tmpfs tmpfs $CHROOT/tmp || clean_exit
if echo "$ARCH" | grep -q "i386"
then linux32 chroot $CHROOT $WD/$0 $WD
else chroot $CHROOT $WD/$0 $WD
fi
umount $CHROOT/proc $CHROOT/sys $CHROOT/tmp $CHROOT/dev $CHROOT/usr/src $CHROOT/home
test -d $WD/$PKG && rm -rf $WD/$PKG
done
fi
......@@ -52,7 +52,7 @@ reverse (invert) the sort order of matches
isolate each command-line parameter from one another; only match if the
files are under different parameter specifications
.TP
.B -L --hardlink
.B -L --linkhard
replace all duplicate files with hardlinks to the first file in each set
of duplicates
.TP
......@@ -96,7 +96,7 @@ Examples section below for further explanation)
.B -r --recurse
for every directory given follow subdirectories encountered within
.TP
.B -s --linksoft
.B -l --linksoft
replace all duplicate files with symlinks to the first file in each set
of duplicates
.TP
......
/* jdupes (C) 2015-2016 Jody Bruchon <jody@jodybruchon.com>
Derived from fdupes (C) 1999-2016 Adrian Lopez
/* jdupes (C) 2015-2017 Jody Bruchon <jody@jodybruchon.com>
Derived from fdupes (C) 1999-2017 Adrian Lopez
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
......@@ -37,11 +37,13 @@
#include <string.h>
#include <errno.h>
#include <libgen.h>
#include <sys/time.h>
#include "jdupes.h"
#include "string_malloc.h"
#include "jody_hash.h"
#include "jody_sort.h"
#include "jody_win_unicode.h"
#include "jody_cacheinfo.h"
#include "version.h"
/* Headers for post-scanning actions */
......@@ -83,51 +85,6 @@ int out_mode = _O_TEXT;
#include "jody_paths.h"
#endif
#define ISFLAG(a,b) ((a & b) == b)
#define SETFLAG(a,b) (a |= b)
#define CLEARFLAG(a,b) (a &= (~b))
/* Low memory option overrides */
#ifdef LOW_MEMORY
#undef DEBUG
#undef LOUD_DEBUG
#undef USE_TREE_REBALANCE
#ifndef NO_PERMS
#define NO_PERMS 1
#endif
#endif
/* Tree rebalance requires tree stats */
#ifdef USE_TREE_REBALANCE
#ifndef TREE_DEPTH_STATS
#define TREE_DEPTH_STATS
#endif
#endif
/* Aggressive verbosity for deep debugging */
#ifdef LOUD_DEBUG
#ifndef DEBUG
#define DEBUG
#endif
#define LOUD(...) if ISFLAG(flags, F_LOUD) __VA_ARGS__
#else
#define LOUD(a)
#endif
/* Compile out debugging stat counters unless requested */
#ifdef DEBUG
#define DBG(a) a
#ifndef TREE_DEPTH_STATS
#define TREE_DEPTH_STATS
#endif
#else
#define DBG(a)
#endif
/* How many operations to wait before updating progress counters */
#define DELAY_COUNT 256
/* Behavior modification flags */
uint_fast32_t flags = 0;
......@@ -154,6 +111,8 @@ static enum {
#define PARTIAL_HASH_SIZE 4096
#endif
static size_t auto_chunk_size;
/* Maximum path buffer size to use; must be large enough for a path plus
* any work that might be done to the array it's stored in. PATH_MAX is
* not always true. Read this article on the false promises of PATH_MAX:
......@@ -237,8 +196,11 @@ struct travdone {
};
static struct travdone *travdone_head = NULL;
static uintmax_t filecount = 0; // Required for progress indicator code
static int did_long_work = 0; // To tell progress indicator to go faster
/* Required for progress indicator code */
static uintmax_t filecount = 0;
static uintmax_t progress = 0, dir_progress = 0, dupecount = 0;
/* Number of read loops before checking progress indicator */
#define CHECK_MINIMUM 256
/* Hash/compare performance statistics (debug mode) */
#ifdef DEBUG
......@@ -273,6 +235,9 @@ static int sort_direction = 1;
/* Signal handler */
static int interrupt = 0;
/* Progress indicator time */
struct timeval time1, time2;
/***** End definitions, begin code *****/
......@@ -283,6 +248,7 @@ void sighandler(const int signum)
(void)signum;
if (interrupt || !ISFLAG(flags, F_SOFTABORT)) {
fprintf(stderr, "\n");
string_malloc_destroy();
exit(EXIT_FAILURE);
}
interrupt = 1;
......@@ -294,10 +260,21 @@ void sighandler(const int signum)
extern void oom(const char * const restrict msg)
{
fprintf(stderr, "\nout of memory: %s\n", msg);
string_malloc_destroy();
exit(EXIT_FAILURE);
}
/* Null pointer failure */
extern void nullptr(const char * restrict func)
{
static const char n[] = "(NULL)";
if (func == NULL) func = n;
fprintf(stderr, "\ninternal error: NULL pointer passed to %s\n", func);
string_malloc_destroy();
exit(EXIT_FAILURE);
}
/* Compare two jody_hashes like memcmp() */
#define HASH_COMPARE(a,b) ((a > b) ? 1:((a == b) ? 0:-1))
......@@ -334,7 +311,7 @@ static int findarg(const char * const arg, const int start,
/* Find the first non-option argument after specified option. */
static int nonoptafter(const char *option, const int argc,
char **oldargv, char **newargv, int optind)
char **oldargv, char **newargv)
{
int x;
int targetind;
......@@ -353,10 +330,39 @@ static int nonoptafter(const char *option, const int argc,
}
/* Update progress indicator if requested */
static void update_progress(const char * const restrict msg, const int file_percent)
{
static int did_fpct = 0;
/* The caller should be doing this anyway...but don't trust that they did */
if (ISFLAG(flags, F_HIDEPROGRESS)) return;
gettimeofday(&time2, NULL);
if (progress == 0 || time2.tv_sec > time1.tv_sec) {
fprintf(stderr, "\rProgress [%ju/%ju, %ju pairs matched] %ju%%", progress, filecount,
dupecount, (progress * 100) / filecount);
if (file_percent > -1 && msg != NULL) {
fprintf(stderr, " (%s: %d%%) ", msg, file_percent);
did_fpct = 1;
} else if (did_fpct != 0) {
fprintf(stderr, " ");
did_fpct = 0;
}
fflush(stderr);
}
time1.tv_sec = time2.tv_sec;
return;
}
/* Check file's stat() info to make sure nothing has changed
* Returns 1 if changed, 0 if not changed, negative if error */
extern int file_has_changed(file_t * const restrict file)
{
if (file == NULL || file->d_name == NULL) nullptr("file_has_changed()");
LOUD(fprintf(stderr, "file_has_changed('%s')\n", file->d_name);)
if (!ISFLAG(file->flags, F_VALID_STAT)) return -66;
#ifdef ON_WINDOWS
......@@ -390,6 +396,9 @@ extern int file_has_changed(file_t * const restrict file)
extern inline int getfilestats(file_t * const restrict file)
{
if (file == NULL || file->d_name == NULL) nullptr("getfilestats()");
LOUD(fprintf(stderr, "getfilestats('%s')\n", file->d_name);)
/* Don't stat the same file more than once */
if (ISFLAG(file->flags, F_VALID_STAT)) return 0;
SETFLAG(file->flags, F_VALID_STAT);
......@@ -427,6 +436,9 @@ extern inline int getfilestats(file_t * const restrict file)
extern int getdirstats(const char * const restrict name,
jdupes_ino_t * const restrict inode, dev_t * const restrict dev)
{
if (name == NULL || inode == NULL || dev == NULL) nullptr("getdirstats");
LOUD(fprintf(stderr, "getdirstats('%s', %p, %p)\n", name, (void *)inode, (void *)dev);)
#ifdef ON_WINDOWS
if (win_stat(name, &ws) != 0) return -1;
*inode = ws.inode;
......@@ -448,11 +460,9 @@ extern int getdirstats(const char * const restrict name,
* 2 on an absolute match condition met */
extern int check_conditions(const file_t * const restrict file1, const file_t * const restrict file2)
{
/* Null pointer sanity check */
if (!file1 || !file2) {
LOUD(fprintf(stderr, "internal error: null pointer passed to check_conditions()\n");)
exit(EXIT_FAILURE);
}
if (file1 == NULL || file2 == NULL || file1->d_name == NULL || file2->d_name == NULL) nullptr("check_conditions()");
LOUD(fprintf(stderr, "check_conditions('%s', '%s')\n", file1->d_name, file2->d_name);)
/* Exclude based on -I/--isolate */
if (ISFLAG(flags, F_ISOLATE) && (file1->user_order == file2->user_order)) {
......@@ -487,11 +497,11 @@ extern int check_conditions(const file_t * const restrict file1, const file_t *
/* Exclude files that are not the same size */
if (file1->size > file2->size) {
LOUD(fprintf(stderr, "check_conditions: no match: file1 > file2 (%jd < %jd)\n", (intmax_t)file1->size, (intmax_t)file2->size));
LOUD(fprintf(stderr, "check_conditions: no match: size of file1 > file2 (%jd > %jd)\n", (intmax_t)file1->size, (intmax_t)file2->size));
return -1;
}
if (file1->size < file2->size) {
LOUD(fprintf(stderr, "check_conditions: no match: file1 < file2 (%jd > %jd)\n", (intmax_t)file1->size, (intmax_t)file2->size));
LOUD(fprintf(stderr, "check_conditions: no match: size of file1 < file2 (%jd < %jd)\n", (intmax_t)file1->size, (intmax_t)file2->size));
return 1;
}
......@@ -501,18 +511,24 @@ extern int check_conditions(const file_t * const restrict file1, const file_t *
}
/* Create a new traversal check object */
static int travdone_alloc(struct travdone **trav, const char * const restrict dir)
/* Create a new traversal check object and initialize its values */
static struct travdone *travdone_alloc(const jdupes_ino_t inode, const dev_t device)
{
*trav = string_malloc(sizeof(struct travdone));
if (*trav == NULL) {
LOUD(fprintf(stderr, "travdone_alloc: malloc returned NULL\n");)
return -1;
struct travdone *trav;
LOUD(fprintf(stderr, "travdone_alloc(%jd, %jd)\n", (intmax_t)inode, (intmax_t)device);)
trav = (struct travdone *)string_malloc(sizeof(struct travdone));
if (trav == NULL) {
LOUD(fprintf(stderr, "travdone_alloc: malloc failed\n");)
return NULL;
}
(*trav)->left = NULL;
(*trav)->right = NULL;
if (getdirstats(dir, &((*trav)->inode), &((*trav)->device)) != 0) return -1;
return 0;
trav->left = NULL;
trav->right = NULL;
trav->inode = inode;
trav->device = device;
LOUD(fprintf(stderr, "travdone_alloc returned %p\n", (void *)trav);)
return trav;
}
......@@ -526,9 +542,7 @@ static void grokdir(const char * const restrict dir,
static struct stat linfo;
#endif
struct dirent *dirinfo;
static uintmax_t progress = 0, dir_progress = 0;
static int grokdir_level = 0;
static int delay = DELAY_COUNT;
static char tempname[PATHBUF_SIZE * 2];
size_t dirlen;
struct travdone *traverse;
......@@ -542,25 +556,28 @@ static void grokdir(const char * const restrict dir,
DIR *cd;
#endif
if (dir == NULL || filelistp == NULL) nullptr("grokdir()");
LOUD(fprintf(stderr, "grokdir: scanning '%s' (order %d)\n", dir, user_dir_count));
/* Double traversal prevention tree */
if (getdirstats(dir, &inode, &device) != 0) goto error_travdone;
if (travdone_head == NULL) {
if (travdone_alloc(&travdone_head, dir) != 0) goto error_travdone;
travdone_head = travdone_alloc(inode, device);
if (travdone_head == NULL) goto error_travdone;
} else {
if (getdirstats(dir, &inode, &device) != 0) goto error_travdone;
traverse = travdone_head;
while (1) {
if (traverse == NULL) nullptr("grokdir() traverse");
/* Don't re-traverse directories we've already seen */
if (inode == traverse->inode && device == traverse->device) {
LOUD(fprintf(stderr, "already seen dir '%s', skipping\n", dir);)
return;
}
if (inode >= traverse->inode) {
} else if (inode > traverse->inode || (inode == traverse->inode && device > traverse->device)) {
/* Traverse right */
if (traverse->right == NULL) {
LOUD(fprintf(stderr, "traverse dir right '%s'\n", dir);)
if (travdone_alloc(&(traverse->right), dir) != 0) goto error_travdone;
traverse->right = travdone_alloc(inode, device);
if (traverse->right == NULL) goto error_travdone;
break;
}
traverse = traverse->right;
......@@ -569,7 +586,8 @@ static void grokdir(const char * const restrict dir,
/* Traverse left */
if (traverse->left == NULL) {
LOUD(fprintf(stderr, "traverse dir left '%s'\n", dir);)
if (travdone_alloc(&(traverse->left), dir) != 0) goto error_travdone;
traverse->left = travdone_alloc(inode, device);
if (traverse->left == NULL) goto error_travdone;
break;
}
traverse = traverse->left;
......@@ -593,7 +611,7 @@ static void grokdir(const char * const restrict dir,
LOUD(fprintf(stderr, "FindFirstFile: %s\n", dir));
hFind = FindFirstFile((LPCWSTR)wname, &ffd);
if (hFind == INVALID_HANDLE_VALUE) { fprintf(stderr, "handle bad\n"); goto error_cd; }
if (hFind == INVALID_HANDLE_VALUE) { fprintf(stderr, "\nfile handle bad\n"); goto error_cd; }
LOUD(fprintf(stderr, "Loop start\n"));
do {
char * restrict tp = tempname;
......@@ -614,11 +632,12 @@ static void grokdir(const char * const restrict dir,
LOUD(fprintf(stderr, "grokdir: readdir: '%s'\n", dirinfo->d_name));
if (strcmp(dirinfo->d_name, ".") && strcmp(dirinfo->d_name, "..")) {
if (!ISFLAG(flags, F_HIDEPROGRESS)) {
if (delay >= DELAY_COUNT) {
delay = 0;
gettimeofday(&time2, NULL);
if (progress == 0 || time2.tv_sec > time1.tv_sec) {
fprintf(stderr, "\rScanning: %ju files, %ju dirs (in %u specified)",
progress, dir_progress, user_dir_count);
} else delay++;
}
time1.tv_sec = time2.tv_sec;
}
/* Assemble the file's full path name, optimized to avoid strcat() */
......@@ -777,16 +796,16 @@ static void grokdir(const char * const restrict dir,
grokdir_level--;
if (grokdir_level == 0 && !ISFLAG(flags, F_HIDEPROGRESS)) {
fprintf(stderr, "\rExamining %ju files, %ju dirs (in %u specified)",
fprintf(stderr, "\rScanning: %ju files, %ju dirs (in %u specified)",
progress, dir_progress, user_dir_count);
}
return;
error_travdone:
fprintf(stderr, "error: could not stat dir "); fwprint(stderr, dir, 1);
fprintf(stderr, "\ncould not stat dir "); fwprint(stderr, dir, 1);
return;
error_cd:
fprintf(stderr, "error: could not chdir to "); fwprint(stderr, dir, 1);
fprintf(stderr, "\ncould not chdir to "); fwprint(stderr, dir, 1);
return;
error_overflow:
fprintf(stderr, "\nerror: a path buffer overflowed\n");
......@@ -802,6 +821,10 @@ static hash_t *get_filehash(const file_t * const restrict checkfile,
static hash_t hash[1];
static hash_t chunk[(CHUNK_SIZE / sizeof(hash_t))];
FILE *file;
int check = 0;
if (checkfile == NULL || checkfile->d_name == NULL) nullptr("get_filehash()");
LOUD(fprintf(stderr, "get_filehash('%s', %jd)\n", checkfile->d_name, (intmax_t)max_read);)
/* Get the file size. If we can't read it, bail out early */
if (checkfile->size == -1) {
......@@ -838,7 +861,7 @@ static hash_t *get_filehash(const file_t * const restrict checkfile,
file = fopen(checkfile->d_name, FILE_MODE_RO);
#endif