Import Upstream version 1.10.3

parent 7ae2b816
jdupes 1.10.3
- Add -M/--printwithsummary option
- Add -0/--printnull option
- Add very long path support on Windows 10
- Do not output progress indicators if output is not a TTY
- Remove an old undocumented long option '--summary'
jdupes 1.10.2
- Add -P/--print option
......
......@@ -86,7 +86,7 @@ ifndef NO_UNICODE
COMPILER_OPTIONS += -municode
endif
COMPILER_OPTIONS += -D__USE_MINGW_ANSI_STDIO=1 -DON_WINDOWS=1
OBJS += win_stat.o
OBJS += win_stat.o winres.o
override undefine ENABLE_BTRFS
endif
......@@ -126,11 +126,14 @@ OBJS += jody_cacheinfo.o
OBJS += act_deletefiles.o act_linkfiles.o act_printmatches.o act_summarize.o
OBJS += $(ADDITIONAL_OBJECTS)
all: jdupes
all: $(PROGRAM_NAME)
jdupes: $(OBJS)
$(PROGRAM_NAME): $(OBJS)
$(CC) $(CFLAGS) $(LDFLAGS) -o $(PROGRAM_NAME) $(OBJS)
winres.o : winres.rc winres.manifest.xml
windres winres.rc winres.o
installdirs:
test -e $(DESTDIR)$(BIN_DIR) || $(MKDIR) $(DESTDIR)$(BIN_DIR)
test -e $(DESTDIR)$(MAN_DIR) || $(MKDIR) $(DESTDIR)$(MAN_DIR)
......
......@@ -70,6 +70,7 @@ Usage
Usage: jdupes [options] DIRECTORY...
-@ --loud output annoying low-level debug info while running
-0 --printnull output nulls instead of CR/LF (like 'find -print0')
-1 --one-file-system do not match files on different filesystems/devices
-A --nohidden exclude hidden files from consideration
-B --dedupe Send matches to btrfs for block-level deduplication
......@@ -90,6 +91,7 @@ Usage: jdupes [options] DIRECTORY...
-l --linksoft make relative symlinks for duplicates w/o prompting
-L --linkhard hard link all duplicate files without prompting
-m --summarize summarize dupe information
-M --printwithsummary will print matches and --summarize at the end
-N --noprompt together with --delete, preserve the first file in
each set of duplicates and delete the rest without
prompting the user
......
......@@ -126,7 +126,7 @@ preserve_none:
printf(" [!] "); fwprint(stdout, dupelist[x]->d_name, 0);
printf("-- file changed since being scanned\n");
#ifdef UNICODE
} else if (DeleteFile(wstr) != 0) {
} else if (DeleteFileW(wstr) != 0) {
#else
} else if (remove(dupelist[x]->d_name) == 0) {
#endif
......
......@@ -196,7 +196,7 @@ extern void linkfiles(file_t *files, const int hard)
fprintf(stderr, "error: MultiByteToWideChar failed: "); fwprint(stderr, srcfile->d_name, 1);
continue;
}
i = MoveFile(wname, wname2) ? 0 : 1;
i = MoveFileW(wname, wname2) ? 0 : 1;
#else
i = rename(dupelist[x]->d_name, temp_path);
#endif
......@@ -205,7 +205,7 @@ extern void linkfiles(file_t *files, const int hard)
fwprint(stderr, dupelist[x]->d_name, 1);
/* Just in case the rename succeeded yet still returned an error, roll back the rename */
#ifdef UNICODE
MoveFile(wname2, wname);
MoveFileW(wname2, wname);
#else
rename(temp_path, dupelist[x]->d_name);
#endif
......@@ -260,7 +260,7 @@ extern void linkfiles(file_t *files, const int hard)
fprintf(stderr, "error: MultiByteToWideChar failed: "); fwprint(stderr, temp_path, 1);
continue;
}
i = MoveFile(wname2, wname) ? 0 : 1;
i = MoveFileW(wname2, wname) ? 0 : 1;
#else
i = rename(temp_path, dupelist[x]->d_name);
#endif
......@@ -278,7 +278,7 @@ extern void linkfiles(file_t *files, const int hard)
fprintf(stderr, "error: MultiByteToWideChar failed: "); fwprint(stderr, temp_path, 1);
continue;
}
i = DeleteFile(wname2) ? 0 : 1;
i = DeleteFileW(wname2) ? 0 : 1;
#else
i = remove(temp_path);
#endif
......@@ -288,7 +288,7 @@ extern void linkfiles(file_t *files, const int hard)
fprintf(stderr, "\nwarning: can't delete temp file, reverting: ");
fwprint(stderr, temp_path, 1);
#ifdef UNICODE
i = DeleteFile(wname) ? 0 : 1;
i = DeleteFileW(wname) ? 0 : 1;
#else
i = remove(dupelist[x]->d_name);
#endif
......@@ -296,7 +296,7 @@ extern void linkfiles(file_t *files, const int hard)
if (i != 0) fprintf(stderr, "\nwarning: couldn't remove link to restore original file\n");
else {
#ifdef UNICODE
i = MoveFile(wname2, wname) ? 0 : 1;
i = MoveFileW(wname2, wname) ? 0 : 1;
#else
i = rename(temp_path, dupelist[x]->d_name);
#endif
......
......@@ -12,23 +12,26 @@ extern void printmatches(file_t * restrict files)
{
file_t * restrict tmpfile;
int printed = 0;
int cr = 1;
LOUD(fprintf(stderr, "act_printmatches: %p\n", files));
if (ISFLAG(flags, F_PRINTNULL)) cr = 2;
while (files != NULL) {
if (ISFLAG(files->flags, F_HAS_DUPES)) {
printed = 1;
if (!ISFLAG(flags, F_OMITFIRST)) {
if (ISFLAG(flags, F_SHOWSIZE)) printf("%" PRIdMAX " byte%c each:\n", (intmax_t)files->size,
(files->size != 1) ? 's' : ' ');
fwprint(stdout, files->d_name, 1);
fwprint(stdout, files->d_name, cr);
}
tmpfile = files->duplicates;
while (tmpfile != NULL) {
fwprint(stdout, tmpfile->d_name, 1);
fwprint(stdout, tmpfile->d_name, cr);
tmpfile = tmpfile->duplicates;
}
if (files->next != NULL) fwprint(stdout, "", 1);
if (files->next != NULL) fwprint(stdout, "", cr);
}
......
......@@ -21,6 +21,12 @@ byte-by-byte comparison.
.B -@ --loud
output annoying low-level debug info while running
.TP
.B -0 --printnull
when printing matches, use null bytes instead of CR/LF bytes, just like
'find -print0' does. This has no effect with any action mode other than
the default "print matches" (delete, link, etc. will still print normal
line endings in the output.)
.TP
.B -1 --one-file-system
do not match files that are on different filesystems or devices
.TP
......@@ -68,7 +74,10 @@ replace all duplicate files with hardlinks to the first file in each set
of duplicates
.TP
.B -m --summarize
summarize duplicate files information
summarize duplicate file information
.TP
.B -M --printwithsummary
print matches and summarize the duplicate file information at the end
.TP
.B -N --noprompt
when used together with \-\-delete, preserve the first file in each set of
......
......@@ -82,13 +82,9 @@
/* Windows + Unicode compilation */
#ifdef UNICODE
wchar_t wname[PATH_MAX];
wchar_t wname2[PATH_MAX];
wchar_t wstr[PATH_MAX];
wpath_t wname,wname2,wstr;
int out_mode = _O_TEXT;
int err_mode = _O_TEXT;
#define M2W(a,b) MultiByteToWideChar(CP_UTF8, 0, a, -1, (LPWSTR)b, PATH_MAX)
#define W2M(a,b) WideCharToMultiByte(CP_UTF8, 0, a, -1, (LPSTR)b, PATH_MAX, NULL, NULL)
#endif /* UNICODE */
#ifndef NO_SYMLINKS
......@@ -891,7 +887,7 @@ static void grokdir(const char * const restrict dir,
if (!M2W(tempname, wname)) goto error_cd;
LOUD(fprintf(stderr, "FindFirstFile: %s\n", dir));
hFind = FindFirstFile((LPCWSTR)wname, &ffd);
hFind = FindFirstFileW(wname, &ffd);
if (hFind == INVALID_HANDLE_VALUE) { LOUD(fprintf(stderr, "\nfile handle bad\n")); goto error_cd; }
LOUD(fprintf(stderr, "Loop start\n"));
do {
......@@ -1008,7 +1004,7 @@ add_single_file:
}
#ifdef UNICODE
while (FindNextFile(hFind, &ffd) != 0);
while (FindNextFileW(hFind, &ffd) != 0);
FindClose(hFind);
#else
closedir(cd);
......@@ -1501,6 +1497,7 @@ static inline void help_text(void)
#ifdef LOUD
printf(" -@ --loud \toutput annoying low-level debug info while running\n");
#endif
printf(" -0 --printnull \toutput nulls instead of CR/LF (like 'find -print0')\n");
printf(" -1 --one-file-system \tdo not match files on different filesystems/devices\n");
printf(" -A --nohidden \texclude hidden files from consideration\n");
#ifdef ENABLE_BTRFS
......@@ -1536,7 +1533,7 @@ static inline void help_text(void)
#endif /* ON_WINDOWS */
#endif /* NO_HARDLINKS */
printf(" -m --summarize \tsummarize dupe information\n");
//printf(" -n --noempty \texclude zero-length files from consideration\n");
printf(" -M --printwithsummary\twill print matches and --summarize at the end\n");
printf(" -N --noprompt \ttogether with --delete, preserve the first file in\n");
printf(" \teach set of duplicates and delete the rest without\n");
printf(" \tprompting the user\n");
......@@ -1603,6 +1600,7 @@ int main(int argc, char **argv)
static const struct option long_options[] =
{
{ "loud", 0, 0, '@' },
{ "printnull", 0, 0, '0' },
{ "one-file-system", 0, 0, '1' },
{ "nohidden", 0, 0, 'A' },
{ "dedupe", 0, 0, 'B' },
......@@ -1617,7 +1615,7 @@ int main(int argc, char **argv)
{ "linksoft", 0, 0, 'l' },
{ "linkhard", 0, 0, 'L' },
{ "summarize", 0, 0, 'm'},
{ "summary", 0, 0, 'm' },
{ "printwithsummary", 0, 0, 'M'},
{ "noempty", 0, 0, 'n' },
{ "noprompt", 0, 0, 'N' },
{ "order", 1, 0, 'o' },
......@@ -1677,17 +1675,27 @@ int main(int argc, char **argv)
auto_chunk_size = (auto_chunk_size + 0x00000fffUL) & 0x000ff000;
#endif /* ON_WINDOWS */
/* Is stderr a terminal? If not, we won't write progress to it */
#ifdef ON_WINDOWS
if (!_isatty(_fileno(stderr))) SETFLAG(flags, F_HIDEPROGRESS);
#else
if (!isatty(fileno(stderr))) SETFLAG(flags, F_HIDEPROGRESS);
#endif
program_name = argv[0];
oldargv = cloneargs(argc, argv);
while ((opt = GETOPT(argc, argv,
"@1ABC:dDfhHiIlLmnNOpP:qQrRsSvzZo:x:X:"
"@01ABC:dDfhHiIlLmMnNOpP:qQrRsSvzZo:x:X:"
#ifndef OMIT_GETOPT_LONG
, long_options, NULL
#endif
)) != EOF) {
switch (opt) {
case '0':
SETFLAG(flags, F_PRINTNULL);
break;
case '1':
SETFLAG(flags, F_ONEFS);
break;
......@@ -1745,6 +1753,10 @@ int main(int argc, char **argv)
case 'm':
SETFLAG(flags, F_SUMMARIZEMATCHES);
break;
case 'M':
SETFLAG(flags, F_SUMMARIZEMATCHES);
SETFLAG(flags, F_PRINTMATCHES);
break;
case 'n':
//fprintf(stderr, "note: -n/--noempty is the default behavior now and is deprecated.\n");
break;
......@@ -1916,7 +1928,7 @@ int main(int argc, char **argv)
!!ISFLAG(flags, F_DEDUPEFILES);
if (pm > 1) {
fprintf(stderr, "Only one of --summarize, --delete, --linkhard, --linksoft, or --dedupe\nmay be used\n");
fprintf(stderr, "Only one of --summarize, --printwithsummary, --delete,\n--linkhard, --linksoft, or --dedupe may be used\n");
string_malloc_destroy();
exit(EXIT_FAILURE);
}
......@@ -2054,7 +2066,6 @@ skip_file_scan:
if (ISFLAG(flags, F_NOPROMPT)) deletefiles(files, 0, 0);
else deletefiles(files, 1, stdin);
}
if (ISFLAG(flags, F_SUMMARIZEMATCHES)) summarizematches(files);
#ifndef NO_SYMLINKS
if (ISFLAG(flags, F_MAKESYMLINKS)) linkfiles(files, 0);
#endif
......@@ -2065,6 +2076,10 @@ skip_file_scan:
if (ISFLAG(flags, F_DEDUPEFILES)) dedupefiles(files);
#endif /* ENABLE_BTRFS */
if (ISFLAG(flags, F_PRINTMATCHES)) printmatches(files);
if (ISFLAG(flags, F_SUMMARIZEMATCHES)) {
if (ISFLAG(flags, F_PRINTMATCHES)) printf("\n\n");
summarizematches(files);
}
string_malloc_destroy();
......
......@@ -92,13 +92,14 @@ extern "C" {
/* Windows + Unicode compilation */
#ifdef UNICODE
extern wchar_t wname[PATH_MAX];
extern wchar_t wname2[PATH_MAX];
extern wchar_t wstr[PATH_MAX];
extern int out_mode;
extern int err_mode;
#define M2W(a,b) MultiByteToWideChar(CP_UTF8, 0, a, -1, (LPWSTR)b, PATH_MAX)
#define W2M(a,b) WideCharToMultiByte(CP_UTF8, 0, a, -1, (LPSTR)b, PATH_MAX, NULL, NULL)
#define WPATH_MAX 8192
#define PATHBUF_SIZE WPATH_MAX
typedef wchar_t wpath_t[WPATH_MAX];
extern wpath_t wname,wname2,wstr;
extern int out_mode;
extern int err_mode;
#define M2W(a,b) MultiByteToWideChar(CP_UTF8, 0, a, -1, (LPWSTR)b, WPATH_MAX)
#define W2M(a,b) WideCharToMultiByte(CP_UTF8, 0, a, -1, (LPSTR)b, WPATH_MAX, NULL, NULL)
#endif /* UNICODE */
#ifndef NO_SYMLINKS
......@@ -166,6 +167,7 @@ extern uint_fast32_t flags;
#define F_MAKESYMLINKS 0x00200000U
#define F_PRINTMATCHES 0x00400000U
#define F_ONEFS 0x00800000U
#define F_PRINTNULL 0x01000000U
#define F_LOUD 0x40000000U
#define F_DEBUG 0x80000000U
......
......@@ -3,12 +3,14 @@
* Copyright (C) 2014-2018 by Jody Bruchon <jody@jodybruchon.com>
* Released under The MIT License
*/
#include "jody_win_unicode.h"
#include "jdupes.h"
#ifdef UNICODE
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#ifdef UNICODE
/* Convert slashes to backslashes in a file path */
extern void slash_convert(char *path)
{
......@@ -23,7 +25,7 @@ extern void slash_convert(char *path)
/* Copy Windows wide character arguments to UTF-8 */
extern void widearg_to_argv(int argc, wchar_t **wargv, char **argv)
{
static char temp[PATH_MAX];
static char temp[PATHBUF_SIZE * 2];
int len;
if (!argv) goto error_bad_argv;
......@@ -45,10 +47,14 @@ error_wc2mb:
exit(EXIT_FAILURE);
}
#else
#define slash_convert(a)
#endif /* UNICODE */
/* Print a string that is wide on Windows but normal on POSIX */
extern int fwprint(FILE * const restrict stream, const char * const restrict str, const int cr)
{
#ifdef UNICODE
int retval;
int stream_mode = out_mode;
......@@ -56,18 +62,19 @@ extern int fwprint(FILE * const restrict stream, const char * const restrict str
if (stream_mode == _O_U16TEXT) {
/* Convert to wide string and send to wide console output */
if (!MultiByteToWideChar(CP_UTF8, 0, str, -1, (LPWSTR)wstr, PATH_MAX)) return -1;
if (!M2W(str,wstr)) return -1;
fflush(stream);
_setmode(_fileno(stream), stream_mode);
retval = fwprintf(stream, L"%S%S", wstr, cr ? L"\n" : L"");
if (cr == 2) retval = fwprintf(stream, L"%S%C", wstr, 0);
else retval = fwprintf(stream, L"%S%S", wstr, cr == 1 ? L"\n" : L"");
fflush(stream);
_setmode(_fileno(stream), _O_TEXT);
return retval;
} else {
return fprintf(stream, "%s%s", str, cr ? "\n" : "");
#endif
if (cr == 2) return fprintf(stream, "%s%c", str, 0);
else return fprintf(stream, "%s%s", str, cr == 1 ? "\n" : "");
#ifdef UNICODE
}
#endif
}
#else
#define fwprint(a,b,c) fprintf(a, "%s%s", b, c ? "\n" : "")
#define slash_convert(a)
#endif /* UNICODE */
......@@ -9,13 +9,14 @@ extern "C" {
#endif
#include "jdupes.h"
#include <stdio.h>
extern int fwprint(FILE * const restrict stream, const char * const restrict str, const int cr);
#ifdef UNICODE
extern void slash_convert(char *path);
extern void widearg_to_argv(int argc, wchar_t **wargv, char **argv);
extern int fwprint(FILE * const restrict stream, const char * const restrict str, const int cr);
#else
#define fwprint(a,b,c) fprintf(a, "%s%s", b, c ? "\n" : "")
#define slash_convert(a)
#endif /* UNICODE */
......
до свидания
\ No newline at end of file
до свидания
\ No newline at end of file
до свидания
\ No newline at end of file
до свидания
\ No newline at end of file
до свидания
\ No newline at end of file
oh hi, this file has a Japanese name for testing this program against!
\ No newline at end of file
......@@ -4,7 +4,7 @@
#ifndef JDUPES_VERSION_H
#define JDUPES_VERSION_H
#define VER "1.10.2"
#define VERDATE "2018-05-24"
#define VER "1.10.3"
#define VERDATE "2018-09-02"
#endif /* JDUPES_VERSION_H */
......@@ -13,6 +13,8 @@
#include "win_stat.h"
#include <stdint.h>
#include "jdupes.h"
/* Convert NT epoch to UNIX epoch */
static time_t nttime_to_unixtime(const uint64_t * const restrict timestamp)
{
......@@ -33,10 +35,9 @@ int win_stat(const char * const filename, struct winstat * const restrict buf)
uint64_t timetemp;
#ifdef UNICODE
static wchar_t wname[PATH_MAX];
if (!buf) return -127;
if (!MultiByteToWideChar(CP_UTF8, 0, filename, -1, wname, PATH_MAX)) return -126;
hFile = CreateFileW(wname, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING,
if (!M2W(filename,wname2)) return -126;
hFile = CreateFileW(wname2, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS, NULL);
#else
if (!buf) return -127;
......
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v3" manifestVersion="1.0">
<assemblyIdentity type="win32" name="jdupes" version="1.10.2.0"/>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
<ws2:longPathAware>true</ws2:longPathAware>
</windowsSettings>
</application>
</assembly>
1 24 winres.manifest.xml
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment