https://git.reactos.org/?p=reactos.git;a=commitdiff;h=7b27e7c4ffa2bab13f79e…
commit 7b27e7c4ffa2bab13f79ef82f3d7e80e9eacfc6d
Author:     Katayama Hirofumi MZ <katayama.hirofumi.mz(a)gmail.com>
AuthorDate: Wed May 5 12:23:16 2021 +0900
Commit:     GitHub <noreply(a)github.com>
CommitDate: Wed May 5 12:23:16 2021 +0900
    [CMDUTILS][FC] Implement FC wildcard handling (#3640)
    Implement wildcard handling on FC (file comparison) command. And fix the bugs on
zero-sized files. CORE-17500
---
 base/applications/cmdutils/fc/CMakeLists.txt |   4 +-
 base/applications/cmdutils/fc/fc.c           | 250 ++++++++++++++++++++++++---
 base/applications/cmdutils/fc/text.h         |  16 +-
 3 files changed, 231 insertions(+), 39 deletions(-)
diff --git a/base/applications/cmdutils/fc/CMakeLists.txt
b/base/applications/cmdutils/fc/CMakeLists.txt
index 4da877ca403..5b91a3bdc7d 100644
--- a/base/applications/cmdutils/fc/CMakeLists.txt
+++ b/base/applications/cmdutils/fc/CMakeLists.txt
@@ -2,6 +2,6 @@ include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/conutils)
 add_executable(fc fc.c texta.c textw.c fc.rc)
 set_module_type(fc win32cui UNICODE)
-target_link_libraries(fc conutils wine ${PSEH_LIB})
-add_importlibs(fc msvcrt user32 kernel32 ntdll)
+target_link_libraries(fc conutils ${PSEH_LIB})
+add_importlibs(fc msvcrt shlwapi 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
index 9350bc1a957..5bcf81d59f6 100644
--- a/base/applications/cmdutils/fc/fc.c
+++ b/base/applications/cmdutils/fc/fc.c
@@ -40,6 +40,8 @@
         va_end(va);
     }
 #endif
+#include <strsafe.h>
+#include <shlwapi.h>
 FCRET NoDifference(VOID)
 {
@@ -271,21 +273,26 @@ static FCRET TextFileCompare(FILECOMPARE *pFC)
             ret = NoDifference();
             break;
         }
-        hMapping0 = CreateFileMappingW(hFile0, NULL, PAGE_READONLY,
-                                       cb0.HighPart, cb0.LowPart, NULL);
-        if (hMapping0 == NULL)
+        if (cb0.QuadPart > 0)
         {
-            ret = CannotRead(pFC->file[0]);
-            break;
+            hMapping0 = CreateFileMappingW(hFile0, NULL, PAGE_READONLY,
+                                           cb0.HighPart, cb0.LowPart, NULL);
+            if (hMapping0 == NULL)
+            {
+                ret = CannotRead(pFC->file[0]);
+                break;
+            }
         }
-        hMapping1 = CreateFileMappingW(hFile1, NULL, PAGE_READONLY,
-                                       cb1.HighPart, cb1.LowPart, NULL);
-        if (hMapping1 == NULL)
+        if (cb1.QuadPart > 0)
         {
-            ret = CannotRead(pFC->file[1]);
-            break;
+            hMapping1 = CreateFileMappingW(hFile1, NULL, PAGE_READONLY,
+                                           cb1.HighPart, cb1.LowPart, NULL);
+            if (hMapping1 == NULL)
+            {
+                ret = CannotRead(pFC->file[1]);
+                break;
+            }
         }
-
         if (fUnicode)
             ret = TextCompareW(pFC, &hMapping0, &cb0, &hMapping1, &cb1);
         else
@@ -330,24 +337,210 @@ static BOOL IsBinaryExt(LPCWSTR filename)
     return FALSE;
 }
-#define HasWildcard(filename) \
-    ((wcschr((filename), L'*') != NULL) || (wcschr((filename), L'?') !=
NULL))
-
 static FCRET FileCompare(FILECOMPARE *pFC)
 {
+    FCRET ret;
     ConResPrintf(StdOut, IDS_COMPARING, pFC->file[0], pFC->file[1]);
     if (!(pFC->dwFlags & FLAG_L) &&
         ((pFC->dwFlags & FLAG_B) || IsBinaryExt(pFC->file[0]) ||
IsBinaryExt(pFC->file[1])))
     {
-        return BinaryFileCompare(pFC);
+        ret = BinaryFileCompare(pFC);
+    }
+    else
+    {
+        ret = TextFileCompare(pFC);
+    }
+
+    ConPuts(StdOut, L"\n");
+    return ret;
+}
+
+/* Is it L"." or L".."? */
+#define IS_DOTS(pch) \
+    ((*(pch) == L'.') && (((pch)[1] == 0) || (((pch)[1] == L'.')
&& ((pch)[2] == 0))))
+#define HasWildcard(filename) \
+    ((wcschr((filename), L'*') != NULL) || (wcschr((filename), L'?') !=
NULL))
+
+static inline BOOL IsTitleWild(LPCWSTR filename)
+{
+    LPCWSTR pch = PathFindFileNameW(filename);
+    return (pch && *pch == L'*' && pch[1] == L'.'
&& !HasWildcard(&pch[2]));
+}
+
+static FCRET FileCompareOneSideWild(const FILECOMPARE *pFC, BOOL bWildRight)
+{
+    FCRET ret = FCRET_IDENTICAL;
+    WIN32_FIND_DATAW find;
+    HANDLE hFind;
+    WCHAR szPath[MAX_PATH];
+    FILECOMPARE fc;
+
+    hFind = FindFirstFileW(pFC->file[bWildRight], &find);
+    if (hFind == INVALID_HANDLE_VALUE)
+    {
+        ConResPrintf(StdErr, IDS_CANNOT_OPEN, pFC->file[bWildRight]);
+        ConPuts(StdOut, L"\n");
+        return FCRET_CANT_FIND;
+    }
+    StringCbCopyW(szPath, sizeof(szPath), pFC->file[bWildRight]);
+
+    fc = *pFC;
+    fc.file[!bWildRight] = pFC->file[!bWildRight];
+    fc.file[bWildRight] = szPath;
+    do
+    {
+        if (IS_DOTS(find.cFileName))
+            continue;
+
+        // replace file title
+        PathRemoveFileSpecW(szPath);
+        PathAppendW(szPath, find.cFileName);
+
+        switch (FileCompare(&fc))
+        {
+            case FCRET_IDENTICAL:
+                break;
+            case FCRET_DIFFERENT:
+                if (ret != FCRET_INVALID)
+                    ret = FCRET_DIFFERENT;
+                break;
+            default:
+                ret = FCRET_INVALID;
+                break;
+        }
+    } while (FindNextFileW(hFind, &find));
+
+    FindClose(hFind);
+    return ret;
+}
+
+static FCRET FileCompareWildTitle(const FILECOMPARE *pFC)
+{
+    FCRET ret = FCRET_IDENTICAL;
+    WIN32_FIND_DATAW find;
+    HANDLE hFind;
+    WCHAR szPath0[MAX_PATH], szPath1[MAX_PATH];
+    FILECOMPARE fc;
+    LPWSTR pch;
+
+    hFind = FindFirstFileW(pFC->file[0], &find);
+    if (hFind == INVALID_HANDLE_VALUE)
+    {
+        ConResPrintf(StdErr, IDS_CANNOT_OPEN, pFC->file[0]);
+        ConPuts(StdOut, L"\n");
+        return FCRET_CANT_FIND;
     }
-    return TextFileCompare(pFC);
+    StringCbCopyW(szPath0, sizeof(szPath0), pFC->file[0]);
+    StringCbCopyW(szPath1, sizeof(szPath1), pFC->file[1]);
+    pch = PathFindExtensionW(pFC->file[1]);
+
+    fc = *pFC;
+    fc.file[0] = szPath0;
+    fc.file[1] = szPath1;
+    do
+    {
+        if (IS_DOTS(find.cFileName))
+            continue;
+
+        // replace file title
+        PathRemoveFileSpecW(szPath0);
+        PathRemoveFileSpecW(szPath1);
+        PathAppendW(szPath0, find.cFileName);
+        PathAppendW(szPath1, find.cFileName);
+
+        // replace dot extension
+        PathRemoveExtensionW(szPath1);
+        PathAddExtensionW(szPath1, pch);
+
+        switch (FileCompare(&fc))
+        {
+            case FCRET_IDENTICAL:
+                break;
+            case FCRET_DIFFERENT:
+                if (ret != FCRET_INVALID)
+                    ret = FCRET_DIFFERENT;
+                break;
+            default:
+                ret = FCRET_INVALID;
+                break;
+        }
+    } while (FindNextFileW(hFind, &find));
+
+    FindClose(hFind);
+    return ret;
+}
+
+static FCRET FileCompareBothWild(const FILECOMPARE *pFC)
+{
+    FCRET ret = FCRET_IDENTICAL;
+    WIN32_FIND_DATAW find0, find1;
+    HANDLE hFind0, hFind1;
+    WCHAR szPath0[MAX_PATH], szPath1[MAX_PATH];
+    FILECOMPARE fc;
+
+    hFind0 = FindFirstFileW(pFC->file[0], &find0);
+    if (hFind0 == INVALID_HANDLE_VALUE)
+    {
+        ConResPrintf(StdErr, IDS_CANNOT_OPEN, pFC->file[0]);
+        ConPuts(StdOut, L"\n");
+        return FCRET_CANT_FIND;
+    }
+    hFind1 = FindFirstFileW(pFC->file[1], &find1);
+    if (hFind1 == INVALID_HANDLE_VALUE)
+    {
+        CloseHandle(hFind0);
+        ConResPrintf(StdErr, IDS_CANNOT_OPEN, pFC->file[1]);
+        ConPuts(StdOut, L"\n");
+        return FCRET_CANT_FIND;
+    }
+    StringCbCopyW(szPath0, sizeof(szPath0), pFC->file[0]);
+    StringCbCopyW(szPath1, sizeof(szPath1), pFC->file[1]);
+
+    fc = *pFC;
+    fc.file[0] = szPath0;
+    fc.file[1] = szPath1;
+    do
+    {
+        while (IS_DOTS(find0.cFileName))
+        {
+            if (!FindNextFileW(hFind0, &find0))
+                goto quit;
+        }
+        while (IS_DOTS(find1.cFileName))
+        {
+            if (!FindNextFileW(hFind1, &find1))
+                goto quit;
+        }
+
+        // replace file title
+        PathRemoveFileSpecW(szPath0);
+        PathRemoveFileSpecW(szPath1);
+        PathAppendW(szPath0, find0.cFileName);
+        PathAppendW(szPath1, find1.cFileName);
+
+        switch (FileCompare(&fc))
+        {
+            case FCRET_IDENTICAL:
+                break;
+            case FCRET_DIFFERENT:
+                if (ret != FCRET_INVALID)
+                    ret = FCRET_DIFFERENT;
+                break;
+            default:
+                ret = FCRET_INVALID;
+                break;
+        }
+    } while (FindNextFileW(hFind0, &find0) && FindNextFileW(hFind1,
&find1));
+quit:
+    CloseHandle(hFind0);
+    CloseHandle(hFind1);
+    return ret;
 }
 static FCRET WildcardFileCompare(FILECOMPARE *pFC)
 {
-    FCRET ret;
+    BOOL fWild0, fWild1;
     if (pFC->dwFlags & FLAG_HELP)
     {
@@ -361,15 +554,24 @@ static FCRET WildcardFileCompare(FILECOMPARE *pFC)
         return FCRET_INVALID;
     }
-    if (HasWildcard(pFC->file[0]) || HasWildcard(pFC->file[1]))
+    fWild0 = HasWildcard(pFC->file[0]);
+    fWild1 = HasWildcard(pFC->file[1]);
+    if (fWild0 && fWild1)
     {
-        // TODO: wildcard
-        ConResPuts(StdErr, IDS_CANT_USE_WILDCARD);
+        if (IsTitleWild(pFC->file[0]) && IsTitleWild(pFC->file[1]))
+            return FileCompareWildTitle(pFC);
+        else
+            return FileCompareBothWild(pFC);
     }
-
-    ret = FileCompare(pFC);
-    ConPuts(StdOut, L"\n");
-    return ret;
+    else if (fWild0)
+    {
+        return FileCompareOneSideWild(pFC, FALSE);
+    }
+    else if (fWild1)
+    {
+        return FileCompareOneSideWild(pFC, TRUE);
+    }
+    return FileCompare(pFC);
 }
 int wmain(int argc, WCHAR **argv)
diff --git a/base/applications/cmdutils/fc/text.h b/base/applications/cmdutils/fc/text.h
index d0f2363f6f9..7a5d7382c59 100644
--- a/base/applications/cmdutils/fc/text.h
+++ b/base/applications/cmdutils/fc/text.h
@@ -5,16 +5,6 @@
  * COPYRIGHT:   Copyright 2021 Katayama Hirofumi MZ (katayama.hirofumi.mz(a)gmail.com)
  */
 #include "fc.h"
-#include <stdio.h>
-
-#ifdef __REACTOS__
-    #include <wine/debug.h>
-    WINE_DEFAULT_DEBUG_CHANNEL(fc);
-#else
-    #define ERR /*empty*/
-    #define WARN /*empty*/
-    #define TRACE /*empty*/
-#endif
 #define IS_SPACE(ch) ((ch) == TEXT(' ') || (ch) == TEXT('\t'))
@@ -297,7 +287,6 @@ ParseLines(const FILECOMPARE *pFC, HANDLE *phMapping,
     {
         bCR = (ichNext > 0) && (psz[ichNext - 1] == TEXT('\r'));
         cchNode = ichNext - ich - bCR;
-        TRACE("ich:%ld, cch:%ld, ichNext:%ld, cchNode:%ld\n", ich, cch,
ichNext, cchNode);
         pszLine = AllocLine(&psz[ich], cchNode);
         node = AllocNode(pszLine, lineno++);
         if (!node || !ConvertNode(pFC, node))
@@ -524,10 +513,10 @@ Resync(FILECOMPARE *pFC, struct list **pptr0, struct list **pptr1)
 static FCRET
 Finalize(FILECOMPARE* pFC, struct list *ptr0, struct list* ptr1, BOOL fDifferent)
 {
-    if (!ptr0 || !ptr1)
+    if (!ptr0 && !ptr1)
     {
         if (fDifferent)
-            return FCRET_DIFFERENT;
+            return Different(pFC->file[0], pFC->file[1]);
         return NoDifference();
     }
     else
@@ -539,6 +528,7 @@ Finalize(FILECOMPARE* pFC, struct list *ptr0, struct list* ptr1, BOOL
fDifferent
     }
 }
+// FIXME: "cmd_apitest fc" has some failures.
 FCRET TextCompare(FILECOMPARE *pFC, HANDLE *phMapping0, const LARGE_INTEGER *pcb0,
                                     HANDLE *phMapping1, const LARGE_INTEGER *pcb1)
 {