Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674/* * 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@gmail.com) */#include "fc.h"
#ifdef __REACTOS__ #include <conutils.h>#else #include <stdio.h> #define ConInitStdStreams() /* empty */ #define StdOut stdout #define StdErr stderr void ConPuts(FILE *fp, LPCWSTR psz) { fputws(psz, fp); } void ConPrintf(FILE *fp, LPCWSTR psz, ...) { va_list va; va_start(va, psz); vfwprintf(fp, psz, va); va_end(va); } void ConResPuts(FILE *fp, UINT nID) { WCHAR sz[MAX_PATH]; LoadStringW(NULL, nID, sz, _countof(sz)); fputws(sz, fp); } void ConResPrintf(FILE *fp, UINT nID, ...) { va_list va; WCHAR sz[MAX_PATH]; va_start(va, nID); LoadStringW(NULL, nID, sz, _countof(sz)); vfwprintf(fp, sz, va); va_end(va); }#endif#include <strsafe.h>#include <shlwapi.h>
FCRET NoDifference(VOID){ ConResPuts(StdOut, IDS_NO_DIFFERENCE); return FCRET_IDENTICAL;}
FCRET Different(LPCWSTR file0, LPCWSTR file1){ ConResPrintf(StdOut, IDS_DIFFERENT, file0, file1); return FCRET_DIFFERENT;}
FCRET LongerThan(LPCWSTR file0, LPCWSTR file1){ ConResPrintf(StdOut, IDS_LONGER_THAN, file0, file1); return FCRET_DIFFERENT;}
FCRET OutOfMemory(VOID){ ConResPuts(StdErr, IDS_OUT_OF_MEMORY); return FCRET_INVALID;}
FCRET CannotRead(LPCWSTR file){ ConResPrintf(StdErr, IDS_CANNOT_READ, file); return FCRET_INVALID;}
FCRET InvalidSwitch(VOID){ ConResPuts(StdErr, IDS_INVALID_SWITCH); return FCRET_INVALID;}
FCRET ResyncFailed(VOID){ ConResPuts(StdOut, IDS_RESYNC_FAILED); return FCRET_DIFFERENT;}
VOID PrintCaption(LPCWSTR file){ ConPrintf(StdOut, L"***** %ls\n", file);}
VOID PrintEndOfDiff(VOID){ ConPuts(StdOut, L"*****\n\n");}
VOID PrintDots(VOID){ ConPuts(StdOut, L"...\n");}
VOID PrintLineW(const FILECOMPARE *pFC, DWORD lineno, LPCWSTR psz){ if (pFC->dwFlags & FLAG_N) ConPrintf(StdOut, L"%5d: %ls\n", lineno, psz); else ConPrintf(StdOut, L"%ls\n", psz);}VOID PrintLineA(const FILECOMPARE *pFC, DWORD lineno, LPCSTR psz){ if (pFC->dwFlags & FLAG_N) ConPrintf(StdOut, L"%5d: %hs\n", lineno, psz); else ConPrintf(StdOut, L"%hs\n", psz);}
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 hFile0, hFile1, hMapping0 = NULL, hMapping1 = NULL; LPBYTE pb0 = NULL, pb1 = NULL; LARGE_INTEGER ib, cb0, cb1, cbCommon; DWORD cbView, ibView; BOOL fDifferent = FALSE;
hFile0 = DoOpenFileForInput(pFC->file[0]); if (hFile0 == INVALID_HANDLE_VALUE) return FCRET_CANT_FIND; hFile1 = DoOpenFileForInput(pFC->file[1]); if (hFile1 == INVALID_HANDLE_VALUE) { CloseHandle(hFile0); return FCRET_CANT_FIND; }
do { if (_wcsicmp(pFC->file[0], pFC->file[1]) == 0) { ret = NoDifference(); break; } if (!GetFileSizeEx(hFile0, &cb0)) { ret = CannotRead(pFC->file[0]); break; } if (!GetFileSizeEx(hFile1, &cb1)) { ret = CannotRead(pFC->file[1]); break; } cbCommon.QuadPart = min(cb0.QuadPart, cb1.QuadPart); if (cbCommon.QuadPart > 0) { 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) { ret = CannotRead(pFC->file[1]); break; }
ret = FCRET_IDENTICAL; for (ib.QuadPart = 0; ib.QuadPart < cbCommon.QuadPart; ) { cbView = (DWORD)min(cbCommon.QuadPart - ib.QuadPart, MAX_VIEW_SIZE); pb0 = MapViewOfFile(hMapping0, FILE_MAP_READ, ib.HighPart, ib.LowPart, cbView); pb1 = MapViewOfFile(hMapping1, FILE_MAP_READ, ib.HighPart, ib.LowPart, cbView); if (!pb0 || !pb1) { ret = OutOfMemory(); break; } for (ibView = 0; ibView < cbView; ++ib.QuadPart, ++ibView) { if (pb0[ibView] == pb1[ibView]) continue;
fDifferent = TRUE; if (cbCommon.QuadPart > MAXDWORD) { ConPrintf(StdOut, L"%016I64X: %02X %02X\n", ib.QuadPart, pb0[ibView], pb1[ibView]); } else { ConPrintf(StdOut, L"%08lX: %02X %02X\n", ib.LowPart, pb0[ibView], pb1[ibView]); } } UnmapViewOfFile(pb0); UnmapViewOfFile(pb1); pb0 = pb1 = NULL; } if (ret != FCRET_IDENTICAL) break; }
if (cb0.QuadPart < cb1.QuadPart) ret = LongerThan(pFC->file[1], pFC->file[0]); else if (cb0.QuadPart > cb1.QuadPart) ret = LongerThan(pFC->file[0], pFC->file[1]); else if (fDifferent) ret = Different(pFC->file[0], pFC->file[1]); else ret = NoDifference(); } while (0);
UnmapViewOfFile(pb0); UnmapViewOfFile(pb1); CloseHandle(hMapping0); CloseHandle(hMapping1); CloseHandle(hFile0); CloseHandle(hFile1); return ret;}
static FCRET TextFileCompare(FILECOMPARE *pFC){ FCRET ret; HANDLE hFile0, hFile1, hMapping0 = NULL, hMapping1 = NULL; LARGE_INTEGER cb0, cb1; BOOL fUnicode = !!(pFC->dwFlags & FLAG_U);
hFile0 = DoOpenFileForInput(pFC->file[0]); if (hFile0 == INVALID_HANDLE_VALUE) return FCRET_CANT_FIND; hFile1 = DoOpenFileForInput(pFC->file[1]); if (hFile1 == INVALID_HANDLE_VALUE) { CloseHandle(hFile0); return FCRET_CANT_FIND; }
do { if (_wcsicmp(pFC->file[0], pFC->file[1]) == 0) { ret = NoDifference(); break; } if (!GetFileSizeEx(hFile0, &cb0)) { ret = CannotRead(pFC->file[0]); break; } if (!GetFileSizeEx(hFile1, &cb1)) { ret = CannotRead(pFC->file[1]); break; } if (cb0.QuadPart == 0 && cb1.QuadPart == 0) { ret = NoDifference(); break; } if (cb0.QuadPart > 0) { hMapping0 = CreateFileMappingW(hFile0, NULL, PAGE_READONLY, cb0.HighPart, cb0.LowPart, NULL); if (hMapping0 == NULL) { ret = CannotRead(pFC->file[0]); break; } } if (cb1.QuadPart > 0) { 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 ret = TextCompareA(pFC, &hMapping0, &cb0, &hMapping1, &cb1); } while (0);
CloseHandle(hMapping0); CloseHandle(hMapping1); CloseHandle(hFile0); CloseHandle(hFile1); 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-commands/fc static const LPCWSTR s_exts[] = { L"EXE", L"COM", L"SYS", L"OBJ", L"LIB", L"BIN" }; size_t iext; LPCWSTR pch, ext, pch0 = wcsrchr(filename, L'\\'), pch1 = wcsrchr(filename, L'/'); if (!pch0 && !pch1) pch = filename; else if (!pch0 && pch1) pch = pch1; else if (pch0 && !pch1) pch = pch0; else if (pch0 < pch1) pch = pch1; else pch = pch0;
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;}
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]))) { 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; } 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){ BOOL fWild0, fWild1;
if (pFC->dwFlags & FLAG_HELP) { ConResPuts(StdOut, IDS_USAGE); return FCRET_INVALID; }
if (!pFC->file[0] || !pFC->file[1]) { ConResPuts(StdErr, IDS_NEEDS_FILES); return FCRET_INVALID; }
fWild0 = HasWildcard(pFC->file[0]); fWild1 = HasWildcard(pFC->file[1]); if (fWild0 && fWild1) { if (IsTitleWild(pFC->file[0]) && IsTitleWild(pFC->file[1])) return FileCompareWildTitle(pFC); else return FileCompareBothWild(pFC); } else if (fWild0) { return FileCompareOneSideWild(pFC, FALSE); } else if (fWild1) { return FileCompareOneSideWild(pFC, TRUE); } return FileCompare(pFC);}
int wmain(int argc, WCHAR **argv){ FILECOMPARE fc = { .dwFlags = 0, .n = 100, .nnnn = 2 }; PWCHAR endptr; INT i;
/* Initialize the Console Standard Streams */ ConInitStdStreams();
for (i = 1; i < argc; ++i) { if (argv[i][0] != L'/') { if (!fc.file[0]) fc.file[0] = argv[i]; else if (!fc.file[1]) fc.file[1] = 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'U': fc.dwFlags |= FLAG_U; 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);}
#ifndef __REACTOS__int main(int argc, char **argv){ INT my_argc; LPWSTR *my_argv = CommandLineToArgvW(GetCommandLineW(), &my_argc); INT ret = wmain(my_argc, my_argv); LocalFree(my_argv); return ret;}#endif