https://git.reactos.org/?p=reactos.git;a=commitdiff;h=8bf471105e2d0bbb3c1d9…
commit 8bf471105e2d0bbb3c1d913647e5ce88d9b3d0d4
Author: Katayama Hirofumi MZ <katayama.hirofumi.mz(a)gmail.com>
AuthorDate: Sun Apr 25 13:06:13 2021 +0900
Commit: GitHub <noreply(a)github.com>
CommitDate: Sun Apr 25 13:06:13 2021 +0900
[CMDUTILS][FC] Initial implement FC command (#3622)
Implement FC (file comparison) command. As a starting point, we support binary mode
comparison at first. Text mode comparison and wildcard are not supported yet. CORE-17500
---
base/applications/cmdutils/CMakeLists.txt | 1 +
base/applications/cmdutils/fc/CMakeLists.txt | 7 +
base/applications/cmdutils/fc/fc.c | 496 +++++++++++++++++++++++++++
base/applications/cmdutils/fc/fc.rc | 12 +
base/applications/cmdutils/fc/lang/en-US.rc | 41 +++
base/applications/cmdutils/fc/resource.h | 13 +
6 files changed, 570 insertions(+)
diff --git a/base/applications/cmdutils/CMakeLists.txt
b/base/applications/cmdutils/CMakeLists.txt
index 43b10eb6c73..f1cd0b9fd1d 100644
--- a/base/applications/cmdutils/CMakeLists.txt
+++ b/base/applications/cmdutils/CMakeLists.txt
@@ -8,6 +8,7 @@ add_subdirectory(cscript)
add_subdirectory(dbgprint)
add_subdirectory(doskey)
add_subdirectory(eventcreate)
+add_subdirectory(fc)
add_subdirectory(find)
add_subdirectory(fsutil)
add_subdirectory(help)
diff --git a/base/applications/cmdutils/fc/CMakeLists.txt
b/base/applications/cmdutils/fc/CMakeLists.txt
new file mode 100644
index 00000000000..fec64446ae1
--- /dev/null
+++ b/base/applications/cmdutils/fc/CMakeLists.txt
@@ -0,0 +1,7 @@
+include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/conutils)
+
+add_executable(fc fc.c fc.rc)
+set_module_type(fc win32cui UNICODE)
+target_link_libraries(fc conutils ${PSEH_LIB})
+add_importlibs(fc msvcrt user32 kernel32)
+add_cd_file(TARGET fc DESTINATION reactos/system32 FOR all)
diff --git a/base/applications/cmdutils/fc/fc.c b/base/applications/cmdutils/fc/fc.c
new file mode 100644
index 00000000000..1697219c6cb
--- /dev/null
+++ b/base/applications/cmdutils/fc/fc.c
@@ -0,0 +1,496 @@
+/*
+ * PROJECT: ReactOS FC Command
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Comparing files
+ * COPYRIGHT: Copyright 2021 Katayama Hirofumi MZ (katayama.hirofumi.mz(a)gmail.com)
+ */
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <windef.h>
+#include <winbase.h>
+#include <winuser.h>
+#include <winnls.h>
+#include <conutils.h>
+#include "resource.h"
+
+// See also:
https://stackoverflow.com/questions/33125766/compare-files-with-a-cmd
+typedef enum FCRET { // return code of FC command
+ FCRET_INVALID = -1, FCRET_IDENTICAL = 0, FCRET_DIFFERENT = 1, FCRET_CANT_FIND = 2
+} FCRET;
+
+#ifdef _WIN64
+ #define MAX_VIEW_SIZE (256 * 1024 * 1024) // 256 MB
+#else
+ #define MAX_VIEW_SIZE (64 * 1024 * 1024) // 64 MB
+#endif
+
+#define FLAG_A (1 << 0)
+#define FLAG_B (1 << 1)
+#define FLAG_C (1 << 2)
+#define FLAG_L (1 << 3)
+#define FLAG_LBn (1 << 4)
+#define FLAG_N (1 << 5)
+#define FLAG_OFFLINE (1 << 6)
+#define FLAG_T (1 << 7)
+#define FLAG_U (1 << 8)
+#define FLAG_W (1 << 9)
+#define FLAG_nnnn (1 << 10)
+#define FLAG_HELP (1 << 11)
+
+typedef struct FILECOMPARE {
+ DWORD dwFlags; // FLAG_...
+ INT n, nnnn;
+ LPCWSTR file1, file2;
+} FILECOMPARE;
+
+static FCRET NoDifference(VOID)
+{
+ ConResPuts(StdOut, IDS_NO_DIFFERENCE);
+ return FCRET_IDENTICAL;
+}
+
+static FCRET Different(LPCWSTR file1, LPCWSTR file2)
+{
+ ConResPrintf(StdOut, IDS_DIFFERENT, file1, file2);
+ return FCRET_DIFFERENT;
+}
+
+static FCRET LongerThan(LPCWSTR file1, LPCWSTR file2)
+{
+ ConResPrintf(StdOut, IDS_LONGER_THAN, file1, file2);
+ return FCRET_DIFFERENT;
+}
+
+static FCRET OutOfMemory(VOID)
+{
+ ConResPuts(StdErr, IDS_OUT_OF_MEMORY);
+ return FCRET_INVALID;
+}
+
+static FCRET CannotRead(LPCWSTR file)
+{
+ ConResPrintf(StdErr, IDS_CANNOT_READ, file);
+ return FCRET_INVALID;
+}
+
+static FCRET InvalidSwitch(VOID)
+{
+ ConResPuts(StdErr, IDS_INVALID_SWITCH);
+ return FCRET_INVALID;
+}
+
+static HANDLE DoOpenFileForInput(LPCWSTR file)
+{
+ HANDLE hFile = CreateFileW(file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
0, NULL);
+ if (hFile == INVALID_HANDLE_VALUE)
+ {
+ ConResPrintf(StdErr, IDS_CANNOT_OPEN, file);
+ }
+ return hFile;
+}
+
+static FCRET BinaryFileCompare(FILECOMPARE *pFC)
+{
+ FCRET ret;
+ HANDLE hFile1, hFile2, hMapping1 = NULL, hMapping2 = NULL;
+ LPBYTE pb1 = NULL, pb2 = NULL;
+ LARGE_INTEGER ib, cb1, cb2, cbCommon;
+ DWORD cbView, ibView;
+ BOOL fDifferent = FALSE;
+
+ hFile1 = DoOpenFileForInput(pFC->file1);
+ if (hFile1 == INVALID_HANDLE_VALUE)
+ return FCRET_CANT_FIND;
+ hFile2 = DoOpenFileForInput(pFC->file2);
+ if (hFile2 == INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(hFile1);
+ return FCRET_CANT_FIND;
+ }
+
+ do
+ {
+ if (_wcsicmp(pFC->file1, pFC->file2) == 0)
+ {
+ ret = NoDifference();
+ break;
+ }
+ if (!GetFileSizeEx(hFile1, &cb1))
+ {
+ ret = CannotRead(pFC->file1);
+ break;
+ }
+ if (!GetFileSizeEx(hFile2, &cb2))
+ {
+ ret = CannotRead(pFC->file2);
+ break;
+ }
+ cbCommon.QuadPart = min(cb1.QuadPart, cb2.QuadPart);
+ if (cbCommon.QuadPart > 0)
+ {
+ hMapping1 = CreateFileMappingW(hFile1, NULL, PAGE_READONLY,
+ cb1.HighPart, cb1.LowPart, NULL);
+ if (hMapping1 == NULL)
+ {
+ ret = CannotRead(pFC->file1);
+ break;
+ }
+ hMapping2 = CreateFileMappingW(hFile2, NULL, PAGE_READONLY,
+ cb2.HighPart, cb2.LowPart, NULL);
+ if (hMapping2 == NULL)
+ {
+ ret = CannotRead(pFC->file2);
+ break;
+ }
+
+ ret = FCRET_IDENTICAL;
+ for (ib.QuadPart = 0; ib.QuadPart < cbCommon.QuadPart; )
+ {
+ cbView = (DWORD)min(cbCommon.QuadPart - ib.QuadPart, MAX_VIEW_SIZE);
+ pb1 = MapViewOfFile(hMapping1, FILE_MAP_READ, ib.HighPart, ib.LowPart,
cbView);
+ pb2 = MapViewOfFile(hMapping2, FILE_MAP_READ, ib.HighPart, ib.LowPart,
cbView);
+ if (!pb1 || !pb2)
+ {
+ ret = OutOfMemory();
+ break;
+ }
+ for (ibView = 0; ibView < cbView; ++ib.QuadPart, ++ibView)
+ {
+ if (pb1[ibView] == pb2[ibView])
+ continue;
+
+ fDifferent = TRUE;
+ if (cbCommon.QuadPart > MAXDWORD)
+ {
+ ConPrintf(StdOut, L"%016I64X: %02X %02X\n",
ib.QuadPart,
+ pb1[ibView], pb2[ibView]);
+ }
+ else
+ {
+ ConPrintf(StdOut, L"%08lX: %02X %02X\n", ib.LowPart,
+ pb1[ibView], pb2[ibView]);
+ }
+ }
+ UnmapViewOfFile(pb1);
+ UnmapViewOfFile(pb2);
+ pb1 = pb2 = NULL;
+ }
+ if (ret != FCRET_IDENTICAL)
+ break;
+ }
+
+ if (cb1.QuadPart < cb2.QuadPart)
+ ret = LongerThan(pFC->file2, pFC->file1);
+ else if (cb1.QuadPart > cb2.QuadPart)
+ ret = LongerThan(pFC->file1, pFC->file2);
+ else if (fDifferent)
+ ret = Different(pFC->file1, pFC->file2);
+ else
+ ret = NoDifference();
+ } while (0);
+
+ UnmapViewOfFile(pb1);
+ UnmapViewOfFile(pb2);
+ CloseHandle(hMapping1);
+ CloseHandle(hMapping2);
+ CloseHandle(hFile1);
+ CloseHandle(hFile2);
+ return ret;
+}
+
+static FCRET
+UnicodeTextCompare(FILECOMPARE *pFC, HANDLE hMapping1, const LARGE_INTEGER *pcb1,
+ HANDLE hMapping2, const LARGE_INTEGER *pcb2)
+{
+ FCRET ret;
+ BOOL fIgnoreCase = !!(pFC->dwFlags & FLAG_C);
+ DWORD dwCmpFlags = (fIgnoreCase ? NORM_IGNORECASE : 0);
+ LPCWSTR psz1, psz2;
+ LARGE_INTEGER cch1 = { .QuadPart = pcb1->QuadPart / sizeof(WCHAR) };
+ LARGE_INTEGER cch2 = { .QuadPart = pcb1->QuadPart / sizeof(WCHAR) };
+
+ do
+ {
+ psz1 = MapViewOfFile(hMapping1, FILE_MAP_READ, 0, 0, pcb1->LowPart);
+ psz2 = MapViewOfFile(hMapping2, FILE_MAP_READ, 0, 0, pcb2->LowPart);
+ if (!psz1 || !psz2)
+ {
+ ret = OutOfMemory();
+ break;
+ }
+ if (cch1.QuadPart < MAXLONG && cch2.QuadPart < MAXLONG)
+ {
+ if (CompareStringW(0, dwCmpFlags, psz1, cch1.LowPart,
+ psz2, cch2.LowPart) == CSTR_EQUAL)
+ {
+ ret = NoDifference();
+ break;
+ }
+ }
+ // TODO: compare each lines
+ // TODO: large file support
+ ret = Different(pFC->file1, pFC->file2);
+ } while (0);
+
+ UnmapViewOfFile(psz1);
+ UnmapViewOfFile(psz2);
+ return ret;
+}
+
+static FCRET
+AnsiTextCompare(FILECOMPARE *pFC, HANDLE hMapping1, const LARGE_INTEGER *pcb1,
+ HANDLE hMapping2, const LARGE_INTEGER *pcb2)
+{
+ FCRET ret;
+ BOOL fIgnoreCase = !!(pFC->dwFlags & FLAG_C);
+ DWORD dwCmpFlags = (fIgnoreCase ? NORM_IGNORECASE : 0);
+ LPSTR psz1, psz2;
+
+ do
+ {
+ psz1 = MapViewOfFile(hMapping1, FILE_MAP_READ, 0, 0, pcb1->LowPart);
+ psz2 = MapViewOfFile(hMapping2, FILE_MAP_READ, 0, 0, pcb2->LowPart);
+ if (!psz1 || !psz2)
+ {
+ ret = OutOfMemory();
+ break;
+ }
+ if (pcb1->QuadPart < MAXLONG && pcb2->QuadPart < MAXLONG)
+ {
+ if (CompareStringA(0, dwCmpFlags, psz1, pcb1->LowPart,
+ psz2, pcb2->LowPart) == CSTR_EQUAL)
+ {
+ ret = NoDifference();
+ break;
+ }
+ }
+ // TODO: compare each lines
+ // TODO: large file support
+ ret = Different(pFC->file1, pFC->file2);
+ } while (0);
+
+ UnmapViewOfFile(psz1);
+ UnmapViewOfFile(psz2);
+ return ret;
+}
+
+static FCRET TextFileCompare(FILECOMPARE *pFC)
+{
+ FCRET ret;
+ HANDLE hFile1, hFile2, hMapping1 = NULL, hMapping2 = NULL;
+ LARGE_INTEGER cb1, cb2;
+ BOOL fUnicode = !!(pFC->dwFlags & FLAG_U);
+
+ hFile1 = DoOpenFileForInput(pFC->file1);
+ if (hFile1 == INVALID_HANDLE_VALUE)
+ return FCRET_CANT_FIND;
+ hFile2 = DoOpenFileForInput(pFC->file2);
+ if (hFile2 == INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(hFile1);
+ return FCRET_CANT_FIND;
+ }
+
+ do
+ {
+ if (_wcsicmp(pFC->file1, pFC->file2) == 0)
+ {
+ ret = NoDifference();
+ break;
+ }
+ if (!GetFileSizeEx(hFile1, &cb1))
+ {
+ ret = CannotRead(pFC->file1);
+ break;
+ }
+ if (!GetFileSizeEx(hFile2, &cb2))
+ {
+ ret = CannotRead(pFC->file2);
+ break;
+ }
+ if (cb1.QuadPart == 0 && cb2.QuadPart == 0)
+ {
+ ret = NoDifference();
+ break;
+ }
+ hMapping1 = CreateFileMappingW(hFile1, NULL, PAGE_READONLY,
+ cb1.HighPart, cb1.LowPart, NULL);
+ if (hMapping1 == NULL)
+ {
+ ret = CannotRead(pFC->file1);
+ break;
+ }
+ hMapping2 = CreateFileMappingW(hFile2, NULL, PAGE_READONLY,
+ cb2.HighPart, cb2.LowPart, NULL);
+ if (hMapping2 == NULL)
+ {
+ ret = CannotRead(pFC->file2);
+ break;
+ }
+
+ if (fUnicode)
+ ret = UnicodeTextCompare(pFC, hMapping1, &cb1, hMapping2, &cb2);
+ else
+ ret = AnsiTextCompare(pFC, hMapping1, &cb1, hMapping2, &cb2);
+ } while (0);
+
+ CloseHandle(hMapping1);
+ CloseHandle(hMapping2);
+ CloseHandle(hFile1);
+ CloseHandle(hFile2);
+ return ret;
+}
+
+static BOOL IsBinaryExt(LPCWSTR filename)
+{
+ // Don't change this array. This is by design.
+ // See also:
https://docs.microsoft.com/en-us/windows-server/administration/windows-comm…
+ static const LPCWSTR s_exts[] = { L"EXE", L"COM",
L"SYS", L"OBJ", L"LIB", L"BIN" };
+ size_t iext;
+ LPCWSTR pch, ext, pch1 = wcsrchr(filename, L'\\'), pch2 = wcsrchr(filename,
L'/');
+ if (!pch1 && !pch2)
+ pch = filename;
+ else if (!pch1 && pch2)
+ pch = pch2;
+ else if (pch1 && !pch2)
+ pch = pch1;
+ else if (pch1 < pch2)
+ pch = pch2;
+ else
+ pch = pch1;
+
+ ext = wcsrchr(pch, L'.');
+ if (ext)
+ {
+ ++ext;
+ for (iext = 0; iext < _countof(s_exts); ++iext)
+ {
+ if (_wcsicmp(ext, s_exts[iext]) == 0)
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+#define HasWildcard(filename) \
+ ((wcschr((filename), L'*') != NULL) || (wcschr((filename), L'?') !=
NULL))
+
+static FCRET FileCompare(FILECOMPARE *pFC)
+{
+ ConResPrintf(StdOut, IDS_COMPARING, pFC->file1, pFC->file2);
+
+ if (!(pFC->dwFlags & FLAG_L) &&
+ ((pFC->dwFlags & FLAG_B) || IsBinaryExt(pFC->file1) ||
IsBinaryExt(pFC->file2)))
+ {
+ return BinaryFileCompare(pFC);
+ }
+ return TextFileCompare(pFC);
+}
+
+static FCRET WildcardFileCompare(FILECOMPARE *pFC)
+{
+ if (pFC->dwFlags & FLAG_HELP)
+ {
+ ConResPuts(StdOut, IDS_USAGE);
+ return FCRET_INVALID;
+ }
+
+ if (!pFC->file1 || !pFC->file2)
+ {
+ ConResPuts(StdErr, IDS_NEEDS_FILES);
+ return FCRET_INVALID;
+ }
+
+ if (HasWildcard(pFC->file1) || HasWildcard(pFC->file2))
+ {
+ // TODO: wildcard
+ ConResPuts(StdErr, IDS_CANT_USE_WILDCARD);
+ }
+
+ return FileCompare(pFC);
+}
+
+int wmain(int argc, WCHAR **argv)
+{
+ FILECOMPARE fc = { .dwFlags = 0, .n = 100, .nnnn = 2 };
+ wchar_t *endptr;
+ INT i;
+
+ /* Initialize the Console Standard Streams */
+ ConInitStdStreams();
+
+ for (i = 1; i < argc; ++i)
+ {
+ if (argv[i][0] != L'/')
+ {
+ if (!fc.file1)
+ fc.file1 = argv[i];
+ else if (!fc.file2)
+ fc.file2 = argv[i];
+ else
+ return InvalidSwitch();
+ continue;
+ }
+ switch (towupper(argv[i][1]))
+ {
+ case L'A':
+ fc.dwFlags |= FLAG_A;
+ break;
+ case L'B':
+ fc.dwFlags |= FLAG_B;
+ break;
+ case L'C':
+ fc.dwFlags |= FLAG_C;
+ break;
+ case L'L':
+ if (_wcsicmp(argv[i], L"/L") == 0)
+ {
+ fc.dwFlags |= FLAG_L;
+ }
+ else if (towupper(argv[i][2]) == L'B')
+ {
+ if (iswdigit(argv[i][3]))
+ {
+ fc.dwFlags |= FLAG_LBn;
+ fc.n = wcstoul(&argv[i][3], &endptr, 10);
+ if (endptr == NULL || *endptr != 0)
+ return InvalidSwitch();
+ }
+ else
+ {
+ return InvalidSwitch();
+ }
+ }
+ break;
+ case L'N':
+ fc.dwFlags |= FLAG_N;
+ break;
+ case L'O':
+ if (_wcsicmp(argv[i], L"/OFF") == 0 || _wcsicmp(argv[i],
L"/OFFLINE") == 0)
+ {
+ fc.dwFlags |= FLAG_OFFLINE;
+ }
+ break;
+ case L'T':
+ fc.dwFlags |= FLAG_T;
+ break;
+ case L'W':
+ fc.dwFlags |= FLAG_W;
+ break;
+ case L'0': case L'1': case L'2': case L'3':
case L'4':
+ case L'5': case L'6': case L'7': case L'8':
case L'9':
+ fc.nnnn = wcstoul(&argv[i][1], &endptr, 10);
+ if (endptr == NULL || *endptr != 0)
+ return InvalidSwitch();
+ fc.dwFlags |= FLAG_nnnn;
+ break;
+ case L'?':
+ fc.dwFlags |= FLAG_HELP;
+ break;
+ default:
+ return InvalidSwitch();
+ }
+ }
+ return WildcardFileCompare(&fc);
+}
diff --git a/base/applications/cmdutils/fc/fc.rc b/base/applications/cmdutils/fc/fc.rc
new file mode 100644
index 00000000000..322c3678dee
--- /dev/null
+++ b/base/applications/cmdutils/fc/fc.rc
@@ -0,0 +1,12 @@
+#include <windef.h>
+#include "resource.h"
+
+#define REACTOS_STR_FILE_DESCRIPTION "ReactOS FC Command"
+#define REACTOS_STR_INTERNAL_NAME "fc"
+#define REACTOS_STR_ORIGINAL_FILENAME "fc.exe"
+#include <reactos/version.rc>
+
+#pragma code_page(65001) /* UTF-8 */
+#ifdef LANGUAGE_EN_US
+ #include "lang/en-US.rc"
+#endif
diff --git a/base/applications/cmdutils/fc/lang/en-US.rc
b/base/applications/cmdutils/fc/lang/en-US.rc
new file mode 100644
index 00000000000..6c8516297b3
--- /dev/null
+++ b/base/applications/cmdutils/fc/lang/en-US.rc
@@ -0,0 +1,41 @@
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+STRINGTABLE
+BEGIN
+ IDS_USAGE "Compares two files or sets of files and displays the differences
between\n\
+them\n\
+\n\
+FC [/A] [/C] [/L] [/LBn] [/N] [/OFF[LINE]] [/T] [/U] [/W] [/nnnn]\n\
+ [drive1:][path1]filename1 [drive2:][path2]filename2\n\
+FC /B [drive1:][path1]filename1 [drive2:][path2]filename2\n\
+\n\
+ /A Displays only first and last lines for each set of differences.\n\
+ /B Performs a binary comparison.\n\
+ /C Disregards the case of letters.\n\
+ /L Compares files as ASCII text.\n\
+ /LBn Sets the maximum consecutive mismatches to the specified\n\
+ number of lines (default: 100).\n\
+ /N Displays the line numbers on an ASCII comparison.\n\
+ /OFF[LINE] Doesn't skip files with offline attribute set.\n\
+ /T Doesn't expand tabs to spaces (default: expand).\n\
+ /U Compare files as UNICODE text files.\n\
+ /W Compresses white space (tabs and spaces) for comparison.\n\
+ /nnnn Specifies the number of consecutive lines that must match\n\
+ after a mismatch (default: 2).\n\
+ [drive1:][path1]filename1\n\
+ Specifies the first file or set of files to compare.\n\
+ [drive2:][path2]filename2\n\
+ Specifies the second file or set of files to compare.\n"
+ IDS_NO_DIFFERENCE "FC: no differences encountered\n"
+ IDS_LONGER_THAN "FC: %ls longer than %ls\n"
+ IDS_COMPARING "Comparing files %ls and %ls\n"
+ IDS_OUT_OF_MEMORY "FC: Out of memory\n"
+ IDS_CANNOT_READ "FC: cannot read from %ls\n"
+ IDS_INVALID_SWITCH "FC: Invalid Switch\n"
+ IDS_CANNOT_OPEN "FC: cannot open %ls - No such file or folder\n"
+ IDS_NEEDS_FILES "FC: Insufficient number of file specifications\n"
+ IDS_CANT_USE_WILDCARD "Wildcard ('*' and '?') are not supported
yet\n"
+ IDS_DIFFERENT "FC: File %ls and %ls are different\n"
+ IDS_TOO_LARGE "FC: File %ls too large\n"
+ IDS_RESYNCH_FAILED "Resynch failed. Files are too different."
+END
diff --git a/base/applications/cmdutils/fc/resource.h
b/base/applications/cmdutils/fc/resource.h
new file mode 100644
index 00000000000..45f8e032bd8
--- /dev/null
+++ b/base/applications/cmdutils/fc/resource.h
@@ -0,0 +1,13 @@
+#define IDS_USAGE 1000
+#define IDS_NO_DIFFERENCE 1001
+#define IDS_LONGER_THAN 1002
+#define IDS_COMPARING 1003
+#define IDS_OUT_OF_MEMORY 1004
+#define IDS_CANNOT_READ 1005
+#define IDS_INVALID_SWITCH 1006
+#define IDS_CANNOT_OPEN 1007
+#define IDS_NEEDS_FILES 1008
+#define IDS_CANT_USE_WILDCARD 1009
+#define IDS_DIFFERENT 1010
+#define IDS_TOO_LARGE 1011
+#define IDS_RESYNCH_FAILED 1012