https://git.reactos.org/?p=reactos.git;a=commitdiff;h=55060911e4070ba3aa82a…
commit 55060911e4070ba3aa82a88042232786866c7d8a
Author: Katayama Hirofumi MZ <katayama.hirofumi.mz(a)gmail.com>
AuthorDate: Tue May 11 14:37:49 2021 +0900
Commit: GitHub <noreply(a)github.com>
CommitDate: Tue May 11 14:37:49 2021 +0900
[CMDUTILS][WHERE] Implement WHERE command (#3642)
WHERE is a Windows command that finds the file location from a executable file name.
This PR implements it in ReactOS. CORE-17443
---
base/applications/cmdutils/CMakeLists.txt | 1 +
base/applications/cmdutils/where/CMakeLists.txt | 7 +
base/applications/cmdutils/where/lang/en-US.rc | 52 +++
base/applications/cmdutils/where/resource.h | 15 +
base/applications/cmdutils/where/strlist.h | 66 ++++
base/applications/cmdutils/where/where.c | 486 ++++++++++++++++++++++++
base/applications/cmdutils/where/where.rc | 12 +
7 files changed, 639 insertions(+)
diff --git a/base/applications/cmdutils/CMakeLists.txt
b/base/applications/cmdutils/CMakeLists.txt
index f1cd0b9fd1d..7f8038789c8 100644
--- a/base/applications/cmdutils/CMakeLists.txt
+++ b/base/applications/cmdutils/CMakeLists.txt
@@ -25,6 +25,7 @@ add_subdirectory(taskkill)
add_subdirectory(tasklist)
add_subdirectory(timeout)
add_subdirectory(tree)
+add_subdirectory(where)
add_subdirectory(whoami)
add_subdirectory(wmic)
add_subdirectory(wscript)
diff --git a/base/applications/cmdutils/where/CMakeLists.txt
b/base/applications/cmdutils/where/CMakeLists.txt
new file mode 100644
index 00000000000..790187f06de
--- /dev/null
+++ b/base/applications/cmdutils/where/CMakeLists.txt
@@ -0,0 +1,7 @@
+include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/conutils)
+
+add_executable(where where.c where.rc)
+set_module_type(where win32cui UNICODE)
+target_link_libraries(where conutils ${PSEH_LIB})
+add_importlibs(where msvcrt kernel32)
+add_cd_file(TARGET where DESTINATION reactos/system32 FOR all)
diff --git a/base/applications/cmdutils/where/lang/en-US.rc
b/base/applications/cmdutils/where/lang/en-US.rc
new file mode 100644
index 00000000000..ce4144fda77
--- /dev/null
+++ b/base/applications/cmdutils/where/lang/en-US.rc
@@ -0,0 +1,52 @@
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+STRINGTABLE
+BEGIN
+ IDS_USAGE "Usage: WHERE [options] pattern...\n\
+\n\
+Description:\n\
+ Shows the location of the file(s) specified by the pattern(s).\n\
+ By default, this tool searches by using the pattern(s) and the paths\n\
+ of the PATH environment variable.\n\
+\n\
+Options:\n\
+ /F Displays all matched file(s) in double quotes.\n\
+ /Q Quiet mode. Doesn't show any files and messages.\n\
+ /R dir Starts searching from the specified directory and recursively\n\
+ performs the search.\n\
+ /T Shows the file size and last modified date of all matched\n\
+ files.\n\
+ pattern Specifies the pattern to search files. Wildcards * and ? can\n\
+ be used. ""$env:pattern"" and
""path:pattern"" formats can also\n\
+ be used, where ""env"" is an environment variable and\n\
+ the search is done in the paths of the ""env""
environment\n\
+ variable. Don't use these formats with /R. The search is also\n\
+ performed by adding the extension of the PATHEXT variable to\n\
+ the pattern.\n\
+ /? Displays this message.\n\
+\n\
+NOTE: This tool returns an error level of 0 if the search was successful,\n\
+ 1 if the file was not found, and 2 if there was an error.\n\
+\n\
+Example:\n\
+ WHERE myfile*.exe\n\
+ WHERE /F /T mspaint\n\
+ WHERE $WINDIR:notepad myfile???\n\
+ WHERE C:\\ReactOS;C:\\ReactOS\\system32:exp*.exe\n\
+ WHERE /R ""C:\\Program Files"" *.dll\n"
+
+ IDS_BAD_ARG "ERROR: Invalid argument - '%ls'.\n"
+ IDS_NOT_FOUND "INFO: Could not find files for the given pattern(s).\n"
+ IDS_FILE_INFO "%10I64u %-12ls %-12ls %ls\n"
+ IDS_WANT_VALUE "ERROR: Value is needed for '%ls'.\n"
+ IDS_TYPE_HELP "Type ""WHERE /?"" for usage help.\n"
+ IDS_ENVPAT_WITH_R "ERROR: ""$env:pattern"" cannot be used
with /R.\n"
+ IDS_PATHPAT_WITH_R "ERROR: ""path:pattern"" format cannot be
used with /R.\n"
+ IDS_BAD_PATHPAT "ERROR: Invalid pattern is specified in
""path:pattern"".\n"
+ IDS_OUTOFMEMORY "ERROR: Out of memory.\n"
+ IDS_BAD_ENVVAR "ERROR: Environment variable ""%ls"" is not
found.\n"
+ IDS_CANT_FOUND "ERROR: The system could not find the file specified.\n"
+ IDS_BAD_DIR "ERROR: Invalid directory is specified.\n"
+ IDS_BAD_NAME "ERROR: The filename, directory name or volume label syntax is
wrong.\n"
+ IDS_TOO_MANY "ERROR: '%ls' option is not allowed more than '%u'
time(s).\n"
+END
diff --git a/base/applications/cmdutils/where/resource.h
b/base/applications/cmdutils/where/resource.h
new file mode 100644
index 00000000000..a1dc35e828b
--- /dev/null
+++ b/base/applications/cmdutils/where/resource.h
@@ -0,0 +1,15 @@
+#define IDS_USAGE 100
+#define IDS_BAD_ARG 101
+#define IDS_NOT_FOUND 103
+#define IDS_FILE_INFO 104
+#define IDS_WANT_VALUE 106
+#define IDS_TYPE_HELP 107
+#define IDS_ENVPAT_WITH_R 108
+#define IDS_PATHPAT_WITH_R 109
+#define IDS_BAD_PATHPAT 110
+#define IDS_OUTOFMEMORY 111
+#define IDS_BAD_ENVVAR 112
+#define IDS_CANT_FOUND 113
+#define IDS_BAD_DIR 114
+#define IDS_BAD_NAME 115
+#define IDS_TOO_MANY 116
diff --git a/base/applications/cmdutils/where/strlist.h
b/base/applications/cmdutils/where/strlist.h
new file mode 100644
index 00000000000..c877ff55863
--- /dev/null
+++ b/base/applications/cmdutils/where/strlist.h
@@ -0,0 +1,66 @@
+/*
+ * PROJECT: ReactOS WHERE command
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Providing string list
+ * COPYRIGHT: Copyright 2021 Katayama Hirofumi MZ
<katayama.hirofumi.mz(a)gmail.com>
+ */
+
+#pragma once
+
+#define str_clone _wcsdup
+
+typedef struct strlist_t
+{
+ LPWSTR *ppsz;
+ unsigned int count;
+} strlist_t;
+#define strlist_default { NULL, 0 }
+
+static inline void strlist_init(strlist_t *plist)
+{
+ plist->ppsz = NULL;
+ plist->count = 0;
+}
+
+static inline LPWSTR strlist_get_at(strlist_t *plist, unsigned int i)
+{
+ return plist->ppsz[i];
+}
+
+static int strlist_add(strlist_t *plist, LPCWSTR psz)
+{
+ LPWSTR *ppsz, clone = str_clone(psz);
+ if (!clone)
+ return 0;
+ ppsz = (LPWSTR *)realloc(plist->ppsz, (plist->count + 1) * sizeof(LPWSTR));
+ if (!ppsz)
+ {
+ free(clone);
+ return 0;
+ }
+ plist->ppsz = ppsz;
+ plist->ppsz[plist->count] = clone;
+ ++(plist->count);
+ return 1;
+}
+
+static void strlist_destroy(strlist_t *plist)
+{
+ unsigned int i;
+ for (i = 0; i < plist->count; ++i)
+ free(plist->ppsz[i]);
+ plist->count = 0;
+ free(plist->ppsz);
+ plist->ppsz = NULL;
+}
+
+static inline int strlist_find_i(strlist_t *plist, LPCWSTR psz)
+{
+ unsigned int i;
+ for (i = 0; i < plist->count; ++i)
+ {
+ if (_wcsicmp(plist->ppsz[i], psz) == 0)
+ return i;
+ }
+ return -1;
+}
diff --git a/base/applications/cmdutils/where/where.c
b/base/applications/cmdutils/where/where.c
new file mode 100644
index 00000000000..f4f68a14944
--- /dev/null
+++ b/base/applications/cmdutils/where/where.c
@@ -0,0 +1,486 @@
+/*
+ * PROJECT: ReactOS WHERE command
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Search executable files
+ * COPYRIGHT: Copyright 2021 Katayama Hirofumi MZ
<katayama.hirofumi.mz(a)gmail.com>
+ */
+
+#include <stdlib.h>
+#include <windef.h>
+#include <winbase.h>
+#include <winnls.h>
+#include <strsafe.h>
+#include <conutils.h>
+#include "strlist.h" // strlist_...
+#include "resource.h"
+
+#define FLAG_HELP (1 << 0) // "/?"
+#define FLAG_R (1 << 1) // recursive directory
+#define FLAG_Q (1 << 2) // quiet mode
+#define FLAG_F (1 << 3) // double quote
+#define FLAG_T (1 << 4) // detailed info
+
+static DWORD s_dwFlags = 0;
+static LPWSTR s_pszRecursiveDir = NULL;
+static strlist_t s_patterns = strlist_default;
+static strlist_t s_results = strlist_default;
+static strlist_t s_pathext = strlist_default;
+
+// is it either "." or ".."?
+#define IS_DOTS(pch) \
+ (*(pch) == L'.' && ((pch)[1] == 0 || ((pch)[1] == L'.'
&& (pch)[2] == 0)))
+
+#define DEFAULT_PATHEXT L".com;.exe;.bat;.cmd"
+
+typedef enum WRET // return code of WHERE command
+{
+ WRET_SUCCESS = 0,
+ WRET_NOT_FOUND = 1,
+ WRET_ERROR = 2
+} WRET;
+
+static VOID WhereError(UINT nID)
+{
+ if (!(s_dwFlags & FLAG_Q)) // not quiet mode?
+ ConResPuts(StdErr, nID);
+}
+
+typedef BOOL (CALLBACK *WHERE_CALLBACK)(LPCWSTR pattern, LPCWSTR path, PWIN32_FIND_DATAW
data);
+
+static BOOL
+WhereSearchGeneric(LPCWSTR pattern, LPWSTR path, size_t path_len, BOOL bDir,
+ WHERE_CALLBACK callback)
+{
+ LPWSTR pch;
+ size_t cch;
+ BOOL ret;
+ WIN32_FIND_DATAW data;
+ HANDLE hFind = FindFirstFileExW(path, FindExInfoStandard, &data,
FindExSearchNameMatch,
+ NULL, 0);
+ if (hFind == INVALID_HANDLE_VALUE)
+ return TRUE; // not found
+
+ pch = wcsrchr(path, L'\\') + 1;
+ cch = path_len - (pch - path);
+ do
+ {
+ if (bDir != !!(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
+ continue;
+ if (bDir && IS_DOTS(data.cFileName))
+ continue; // ignore "." and ".."
+ if (data.dwFileAttributes & FILE_ATTRIBUTE_VIRTUAL)
+ continue; // ignore virtual
+
+ StringCchCopyW(pch, cch, data.cFileName); // build full path
+
+ ret = callback(pattern, path, &data);
+ if (!ret) // out of memory
+ break;
+ } while (FindNextFileW(hFind, &data));
+ FindClose(hFind);
+ return ret;
+}
+
+static BOOL CALLBACK WherePrintPath(LPCWSTR pattern, LPCWSTR path, PWIN32_FIND_DATAW
data)
+{
+ WCHAR szPath[MAX_PATH + 2], szDate[32], szTime[32];
+ LARGE_INTEGER FileSize;
+ FILETIME ftLocal;
+ SYSTEMTIME st;
+
+ if (strlist_find_i(&s_results, path) >= 0)
+ return TRUE; // already exists
+ if (!strlist_add(&s_results, path))
+ return FALSE; // out of memory
+ if (s_dwFlags & FLAG_Q) // quiet mode?
+ return TRUE;
+
+ if (s_dwFlags & FLAG_T) // print detailed info
+ {
+ // convert date/time
+ FileTimeToLocalFileTime(&data->ftLastWriteTime, &ftLocal);
+ FileTimeToSystemTime(&ftLocal, &st);
+ // get date/time strings
+ GetDateFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, szDate, _countof(szDate));
+ GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, szTime, _countof(szTime));
+ // set size
+ FileSize.LowPart = data->nFileSizeLow;
+ FileSize.HighPart = data->nFileSizeHigh;
+ // print
+ if (s_dwFlags & FLAG_F) // double quote
+ StringCchPrintfW(szPath, _countof(szPath), L"\"%s\"",
path);
+ else
+ StringCchCopyW(szPath, _countof(szPath), path);
+ ConResPrintf(StdOut, IDS_FILE_INFO, FileSize.QuadPart, szDate, szTime, szPath);
+ }
+ else // print path only
+ {
+ if (s_dwFlags & FLAG_F) // double quote
+ ConPrintf(StdOut, L"\"%ls\"\n", path);
+ else
+ ConPrintf(StdOut, L"%ls\n", path);
+ }
+ return TRUE; // success
+}
+
+static BOOL WhereSearchFiles(LPCWSTR pattern, LPCWSTR dir)
+{
+ INT iExt;
+ size_t cch;
+ WCHAR szPath[MAX_PATH];
+ StringCchCopyW(szPath, _countof(szPath), dir);
+ StringCchCatW(szPath, _countof(szPath), L"\\");
+ StringCchCatW(szPath, _countof(szPath), pattern);
+ cch = wcslen(szPath);
+
+ for (iExt = 0; iExt < s_pathext.count; ++iExt)
+ {
+ szPath[cch] = 0; // cut off extension
+ // append extension
+ StringCchCatW(szPath, _countof(szPath), strlist_get_at(&s_pathext, iExt));
+
+ if (!WhereSearchGeneric(pattern, szPath, _countof(szPath), FALSE,
WherePrintPath))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static BOOL WhereSearchRecursive(LPCWSTR pattern, LPCWSTR dir);
+
+static BOOL CALLBACK
+WhereSearchRecursiveCallback(LPCWSTR pattern, LPCWSTR path, PWIN32_FIND_DATAW data)
+{
+ return WhereSearchRecursive(pattern, path);
+}
+
+// FIXME: Too slow. Optimize for speed.
+static BOOL WhereSearchRecursive(LPCWSTR pattern, LPCWSTR dir)
+{
+ WCHAR szPath[MAX_PATH];
+ if (!WhereSearchFiles(pattern, dir))
+ return FALSE; // out of memory
+
+ // build path with wildcard
+ StringCchCopyW(szPath, _countof(szPath), dir);
+ StringCchCatW(szPath, _countof(szPath), L"\\*");
+ return WhereSearchGeneric(pattern, szPath, _countof(szPath), TRUE,
+ WhereSearchRecursiveCallback);
+}
+
+static BOOL WhereSearch(LPCWSTR pattern, strlist_t *dirlist)
+{
+ UINT iDir;
+ for (iDir = 0; iDir < dirlist->count; ++iDir)
+ {
+ if (!WhereSearchFiles(pattern, strlist_get_at(dirlist, iDir)))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static BOOL WhereGetVariable(LPCWSTR name, LPWSTR *value)
+{
+ DWORD cch = GetEnvironmentVariableW(name, NULL, 0);
+ if (cch == 0) // variable not found
+ {
+ *value = NULL;
+ if (!(s_dwFlags & FLAG_Q)) // not quiet mode?
+ ConResPrintf(StdErr, IDS_BAD_ENVVAR, name);
+ return TRUE; // it is error, but continue the task
+ }
+
+ *value = malloc(cch * sizeof(WCHAR));
+ if (!*value || !GetEnvironmentVariableW(name, *value, cch))
+ {
+ free(*value);
+ *value = NULL;
+ return FALSE; // error
+ }
+ return TRUE;
+}
+
+static BOOL WhereDoOption(DWORD flag, LPCWSTR option)
+{
+ if (s_dwFlags & flag)
+ {
+ ConResPrintf(StdErr, IDS_TOO_MANY, option, 1);
+ ConResPuts(StdErr, IDS_TYPE_HELP);
+ return FALSE;
+ }
+ s_dwFlags |= flag;
+ return TRUE;
+}
+
+static BOOL WhereParseCommandLine(INT argc, WCHAR** argv)
+{
+ INT iArg;
+ for (iArg = 1; iArg < argc; ++iArg)
+ {
+ LPWSTR arg = argv[iArg];
+ if (arg[0] == L'/' || arg[0] == L'-')
+ {
+ if (arg[2] == 0)
+ {
+ switch (towupper(arg[1]))
+ {
+ case L'?':
+ if (!WhereDoOption(FLAG_HELP, L"/?"))
+ return FALSE;
+ continue;
+ case L'F':
+ if (!WhereDoOption(FLAG_F, L"/F"))
+ return FALSE;
+ continue;
+ case L'Q':
+ if (!WhereDoOption(FLAG_Q, L"/Q"))
+ return FALSE;
+ continue;
+ case L'T':
+ if (!WhereDoOption(FLAG_T, L"/T"))
+ return FALSE;
+ continue;
+ case L'R':
+ {
+ if (!WhereDoOption(FLAG_R, L"/R"))
+ return FALSE;
+ if (iArg + 1 < argc)
+ {
+ ++iArg;
+ s_pszRecursiveDir = argv[iArg];
+ continue;
+ }
+ ConResPrintf(StdErr, IDS_WANT_VALUE, L"/R");
+ ConResPuts(StdErr, IDS_TYPE_HELP);
+ return FALSE;
+ }
+ }
+ }
+ ConResPrintf(StdErr, IDS_BAD_ARG, argv[iArg]);
+ ConResPuts(StdErr, IDS_TYPE_HELP);
+ return FALSE;
+ }
+ else // pattern?
+ {
+ if (!strlist_add(&s_patterns, argv[iArg])) // append pattern
+ {
+ ConResPuts(StdErr, IDS_OUTOFMEMORY);
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE; // success
+}
+
+static BOOL WhereGetPathExt(strlist_t *ext_list)
+{
+ BOOL ret = TRUE;
+ LPWSTR pszPathExt, ext;
+ DWORD cchPathExt = GetEnvironmentVariableW(L"PATHEXT", NULL, 0);
+
+ pszPathExt = (cchPathExt ? malloc(cchPathExt * sizeof(WCHAR)) :
str_clone(DEFAULT_PATHEXT));
+ if (!pszPathExt)
+ return FALSE; // out of memory
+
+ if (cchPathExt)
+ GetEnvironmentVariableW(L"PATHEXT", pszPathExt, cchPathExt);
+
+ if (!strlist_add(ext_list, L"")) // add empty extension for normal search
+ {
+ strlist_destroy(ext_list);
+ free(pszPathExt);
+ return FALSE;
+ }
+
+ for (ext = wcstok(pszPathExt, L";"); ext; ext = wcstok(NULL,
L";")) // for all extensions
+ {
+ if (!strlist_add(ext_list, ext)) // add extension to ext_list
+ {
+ strlist_destroy(ext_list);
+ ret = FALSE;
+ break;
+ }
+ }
+
+ free(pszPathExt);
+ return ret;
+}
+
+static BOOL WhereFindByDirs(LPCWSTR pattern, LPWSTR dirs)
+{
+ BOOL ret;
+ size_t cch;
+ WCHAR szPath[MAX_PATH];
+ LPWSTR dir, pch;
+ strlist_t dirlist = strlist_default;
+
+ GetCurrentDirectoryW(_countof(szPath), szPath);
+ if (!strlist_add(&dirlist, szPath))
+ return FALSE; // out of memory
+
+ for (dir = wcstok(dirs, L";"); dir; dir = wcstok(NULL, L";"))
+ {
+ if (*dir == L'"') // began from '"'
+ {
+ pch = wcschr(++dir, L'"'); // find '"'
+ if (*pch)
+ *pch = 0; // cut off
+ }
+
+ if (*dir != '\\' && dir[1] != L':')
+ continue; // relative path
+
+ cch = wcslen(dir);
+ if (cch > 0 && dir[cch - 1] == L'\\')
+ dir[cch - 1] = 0; // remove trailing backslash
+
+ if (!strlist_add(&dirlist, dir))
+ {
+ strlist_destroy(&dirlist);
+ return FALSE; // out of memory
+ }
+ }
+
+ ret = WhereSearch(pattern, &dirlist);
+ strlist_destroy(&dirlist);
+ return ret;
+}
+
+static BOOL WhereFindByVar(LPCWSTR pattern, LPCWSTR name)
+{
+ LPWSTR value;
+ BOOL ret = WhereGetVariable(name, &value);
+ if (ret && value)
+ ret = WhereFindByDirs(pattern, value);
+ free(value);
+ return ret;
+}
+
+static BOOL WhereIsRecursiveDirOK(LPCWSTR name)
+{
+ if (wcschr(name, L';') != NULL)
+ {
+ WhereError(IDS_BAD_NAME);
+ return FALSE;
+ }
+ else
+ {
+ DWORD attrs = GetFileAttributesW(name);
+ if (attrs == INVALID_FILE_ATTRIBUTES) // file not found
+ {
+ WhereError(IDS_CANT_FOUND);
+ return FALSE;
+ }
+ if (!(attrs & FILE_ATTRIBUTE_DIRECTORY))
+ {
+ WhereError(IDS_BAD_DIR);
+ return FALSE;
+ }
+ return TRUE;
+ }
+}
+
+static BOOL WhereDoPattern(LPWSTR pattern)
+{
+ BOOL ret;
+ LPWSTR pch = wcsrchr(pattern, L':');
+ if (pch)
+ {
+ *pch++ = 0;
+ if (pattern[0] == L'$') // $env:pattern
+ {
+ if (s_dwFlags & FLAG_R) // recursive?
+ {
+ WhereError(IDS_ENVPAT_WITH_R);
+ return FALSE;
+ }
+ ret = WhereFindByVar(pch, pattern + 1);
+ }
+ else // path:pattern
+ {
+ if (s_dwFlags & FLAG_R) // recursive?
+ {
+ WhereError(IDS_PATHPAT_WITH_R);
+ return FALSE;
+ }
+ if (wcschr(pch, L'\\') != NULL) // found '\\'?
+ {
+ WhereError(IDS_BAD_PATHPAT);
+ return FALSE;
+ }
+ ret = WhereFindByDirs(pch, pattern);
+ }
+ }
+ else if (s_pszRecursiveDir) // recursive
+ {
+ WCHAR szPath[MAX_PATH];
+
+ if (!WhereIsRecursiveDirOK(s_pszRecursiveDir))
+ return FALSE;
+
+ GetFullPathNameW(s_pszRecursiveDir, _countof(szPath), szPath, NULL);
+
+ ret = WhereSearchRecursive(pattern, szPath);
+ }
+ else // otherwise
+ {
+ ret = WhereFindByVar(pattern, L"PATH");
+ }
+
+ if (!ret)
+ WhereError(IDS_OUTOFMEMORY);
+ return ret;
+}
+
+INT wmain(INT argc, WCHAR **argv)
+{
+ typedef BOOL (WINAPI *FN_DISABLE_WOW)(PVOID *);
+ HANDLE hKernel32 = GetModuleHandleA("kernel32");
+ FN_DISABLE_WOW DisableWOW =
+ (FN_DISABLE_WOW)GetProcAddress(hKernel32,
"Wow64DisableWow64FsRedirection");
+ DWORD iPattern;
+ WRET ret = WRET_ERROR;
+ PVOID dummy;
+
+ ConInitStdStreams(); // Initialize the Console Standard Streams
+
+ if (!WhereParseCommandLine(argc, argv))
+ goto quit;
+
+ if ((s_dwFlags & FLAG_HELP) || !s_patterns.count)
+ {
+ ConResPuts(StdOut, IDS_USAGE);
+ goto quit;
+ }
+
+ if (DisableWOW)
+ DisableWOW(&dummy);
+
+ if (!WhereGetPathExt(&s_pathext))
+ {
+ WhereError(IDS_OUTOFMEMORY);
+ goto quit;
+ }
+
+ ret = WRET_SUCCESS;
+ for (iPattern = 0; iPattern < s_patterns.count; ++iPattern)
+ {
+ if (!WhereDoPattern(strlist_get_at(&s_patterns, iPattern)))
+ {
+ ret = WRET_ERROR;
+ goto quit;
+ }
+ }
+
+ if (!s_results.count)
+ {
+ WhereError(IDS_NOT_FOUND);
+ ret = WRET_NOT_FOUND;
+ }
+
+quit:
+ strlist_destroy(&s_results);
+ strlist_destroy(&s_patterns);
+ strlist_destroy(&s_pathext);
+ return ret;
+}
diff --git a/base/applications/cmdutils/where/where.rc
b/base/applications/cmdutils/where/where.rc
new file mode 100644
index 00000000000..24452c476b5
--- /dev/null
+++ b/base/applications/cmdutils/where/where.rc
@@ -0,0 +1,12 @@
+#include <windef.h>
+#include "resource.h"
+
+#define REACTOS_STR_FILE_DESCRIPTION "ReactOS WHERE Command"
+#define REACTOS_STR_INTERNAL_NAME "where"
+#define REACTOS_STR_ORIGINAL_FILENAME "where.exe"
+#include <reactos/version.rc>
+
+#pragma code_page(65001) /* UTF-8 */
+#ifdef LANGUAGE_EN_US
+ #include "lang/en-US.rc"
+#endif