https://git.reactos.org/?p=reactos.git;a=commitdiff;h=55060911e4070ba3aa82a88042232786866c7d8a

commit 55060911e4070ba3aa82a88042232786866c7d8a
Author:     Katayama Hirofumi MZ <[email protected]>
AuthorDate: Tue May 11 14:37:49 2021 +0900
Commit:     GitHub <[email protected]>
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 
<[email protected]>
+ */
+
+#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 
<[email protected]>
+ */
+
+#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

Reply via email to