https://git.reactos.org/?p=reactos.git;a=commitdiff;h=d8f9f7f2566614d8c9800…
commit d8f9f7f2566614d8c9800edeffafc086836a3cef
Author: Eric Kohl <eric.kohl(a)reactos.org>
AuthorDate: Sat Jul 15 12:27:09 2023 +0200
Commit: Eric Kohl <eric.kohl(a)reactos.org>
CommitDate: Sat Jul 15 12:27:09 2023 +0200
[NETSH] Replace the wine stub by a slightly more functional version
- Implement a basic command interpreter.
- Add basic support for helper dlls and contexts.
- Add interactive help system with context support.
Everything is still under construction and subject to change.
---
base/applications/network/netsh/CMakeLists.txt | 27 +-
base/applications/network/netsh/context.c | 374 ++++++++++++++++
base/applications/network/netsh/help.c | 152 +++++++
base/applications/network/netsh/helper.c | 593 +++++++++++++++++++++++++
base/applications/network/netsh/interpreter.c | 238 ++++++++++
base/applications/network/netsh/lang/en-US.rc | 36 ++
base/applications/network/netsh/netsh.c | 267 ++++++++++-
base/applications/network/netsh/netsh.rc | 15 +
base/applications/network/netsh/netsh.spec | 7 +
base/applications/network/netsh/precomp.h | 205 +++++++++
base/applications/network/netsh/resource.h | 37 ++
11 files changed, 1923 insertions(+), 28 deletions(-)
diff --git a/base/applications/network/netsh/CMakeLists.txt
b/base/applications/network/netsh/CMakeLists.txt
index 5e4005282da..0c838e60c7c 100644
--- a/base/applications/network/netsh/CMakeLists.txt
+++ b/base/applications/network/netsh/CMakeLists.txt
@@ -1,8 +1,25 @@
-add_definitions(-D__WINESRC__)
-include_directories(${REACTOS_SOURCE_DIR}/sdk/include/wine)
-add_executable(netsh netsh.c)
-target_link_libraries(netsh wine)
+include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/conutils)
+spec2def(netsh.exe netsh.spec ADD_IMPORTLIB)
+
+list(APPEND SOURCE
+ context.c
+ help.c
+ helper.c
+ interpreter.c
+ netsh.c
+ precomp.h)
+
+add_executable(netsh ${SOURCE} netsh.rc ${CMAKE_CURRENT_BINARY_DIR}/netsh.def)
+
+set_target_properties(netsh
+ PROPERTIES
+ ENABLE_EXPORTS TRUE
+ DEFINE_SYMBOL "")
+
set_module_type(netsh win32cui UNICODE)
-add_importlibs(netsh msvcrt kernel32 ntdll)
+target_link_libraries(netsh conutils ${PSEH_LIB})
+add_importlibs(netsh advapi32 msvcrt user32 kernel32 ntdll)
+
+add_pch(netsh precomp.h SOURCE)
add_cd_file(TARGET netsh DESTINATION reactos/system32 FOR all)
diff --git a/base/applications/network/netsh/context.c
b/base/applications/network/netsh/context.c
new file mode 100644
index 00000000000..dded888c879
--- /dev/null
+++ b/base/applications/network/netsh/context.c
@@ -0,0 +1,374 @@
+/*
+ * PROJECT: ReactOS NetSh
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Network Shell context management functions
+ * COPYRIGHT: Copyright 2023 Eric Kohl <eric.kohl(a)reactos.org>
+ */
+
+/* INCLUDES *******************************************************************/
+
+#include "precomp.h"
+
+#define NDEBUG
+#include <debug.h>
+
+/* GLOBALS ********************************************************************/
+
+PCONTEXT_ENTRY pRootContext = NULL;
+PCONTEXT_ENTRY pCurrentContext = NULL;
+
+/* FUNCTIONS ******************************************************************/
+
+PCONTEXT_ENTRY
+AddContext(
+ PCONTEXT_ENTRY pParentContext,
+ PWSTR pszName,
+ GUID *pGuid)
+{
+ PCONTEXT_ENTRY pEntry;
+
+ if (pParentContext != NULL && pszName == NULL)
+ return NULL;
+
+ /* Allocate the entry */
+ pEntry = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CONTEXT_ENTRY));
+ if (pEntry == NULL)
+ return NULL;
+
+ /* Allocate the name buffer */
+ if (pszName != NULL)
+ {
+ pEntry->pszContextName = HeapAlloc(GetProcessHeap(),
+ HEAP_ZERO_MEMORY,
+ (wcslen(pszName) + 1) * sizeof(WCHAR));
+ if (pEntry->pszContextName == NULL)
+ {
+ HeapFree(GetProcessHeap(), 0, pEntry);
+ return NULL;
+ }
+
+ /* Fill the entry */
+ wcscpy(pEntry->pszContextName, pszName);
+ }
+
+ pEntry->pParentContext = pParentContext;
+ if (pGuid != NULL)
+ CopyMemory(&pEntry->Guid, pGuid, sizeof(pEntry->Guid));
+
+ /* Insert it */
+ if (pParentContext != NULL)
+ {
+ if (pParentContext->pSubContextHead == NULL &&
pParentContext->pSubContextTail == NULL)
+ {
+ pParentContext->pSubContextHead = pEntry;
+ pParentContext->pSubContextTail = pEntry;
+ }
+ else
+ {
+ pEntry->pPrev = pParentContext->pSubContextTail;
+ pParentContext->pSubContextTail->pNext = pEntry;
+ pParentContext->pSubContextTail = pEntry;
+ }
+ }
+
+ return pEntry;
+}
+
+
+PCOMMAND_ENTRY
+AddContextCommand(
+ PCONTEXT_ENTRY pContext,
+ LPCWSTR pwszCmdToken,
+ PFN_HANDLE_CMD pfnCmdHandler,
+ DWORD dwShortCmdHelpToken,
+ DWORD dwCmdHlpToken,
+ DWORD dwFlags)
+{
+ PCOMMAND_ENTRY pEntry;
+
+ if (pfnCmdHandler == NULL)
+ return NULL;
+
+ /* Allocate the entry */
+ pEntry = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(COMMAND_ENTRY));
+ if (pEntry == NULL)
+ return NULL;
+
+ pEntry->pwszCmdToken = HeapAlloc(GetProcessHeap(),
+ HEAP_ZERO_MEMORY,
+ (wcslen(pwszCmdToken) + 1) * sizeof(WCHAR));
+ if (pEntry->pwszCmdToken == NULL)
+ {
+ HeapFree(GetProcessHeap(), 0, pEntry);
+ return NULL;
+ }
+
+ wcscpy((LPWSTR)pEntry->pwszCmdToken, pwszCmdToken);
+
+ pEntry->pfnCmdHandler = pfnCmdHandler;
+ pEntry->dwShortCmdHelpToken = dwShortCmdHelpToken;
+ pEntry->dwCmdHlpToken = dwCmdHlpToken;
+ pEntry->dwFlags = dwFlags;
+
+ if (pContext->pCommandListHead == NULL && pContext->pCommandListTail ==
NULL)
+ {
+ pContext->pCommandListHead = pEntry;
+ pContext->pCommandListTail = pEntry;
+ }
+ else
+ {
+ pEntry->pPrev = pContext->pCommandListTail;
+ pContext->pCommandListTail->pNext = pEntry;
+ pContext->pCommandListTail = pEntry;
+ }
+
+ return pEntry;
+}
+
+
+PCOMMAND_GROUP
+AddCommandGroup(
+ PCONTEXT_ENTRY pContext,
+ LPCWSTR pwszCmdGroupToken,
+ DWORD dwShortCmdHelpToken,
+ DWORD dwFlags)
+{
+ PCOMMAND_GROUP pEntry;
+
+ /* Allocate the entry */
+ pEntry = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(COMMAND_GROUP));
+ if (pEntry == NULL)
+ return NULL;
+
+ pEntry->pwszCmdGroupToken = HeapAlloc(GetProcessHeap(),
+ HEAP_ZERO_MEMORY,
+ (wcslen(pwszCmdGroupToken) + 1) *
sizeof(WCHAR));
+ if (pEntry->pwszCmdGroupToken == NULL)
+ {
+ HeapFree(GetProcessHeap(), 0, pEntry);
+ return NULL;
+ }
+
+ wcscpy((LPWSTR)pEntry->pwszCmdGroupToken, pwszCmdGroupToken);
+ pEntry->dwShortCmdHelpToken = dwShortCmdHelpToken;
+ pEntry->dwFlags = dwFlags;
+
+ if (pContext->pGroupListHead == NULL && pContext->pGroupListTail ==
NULL)
+ {
+ pContext->pGroupListHead = pEntry;
+ pContext->pGroupListTail = pEntry;
+ }
+ else
+ {
+ pEntry->pPrev = pContext->pGroupListTail;
+ pContext->pGroupListTail->pNext = pEntry;
+ pContext->pGroupListTail = pEntry;
+ }
+
+ return pEntry;
+}
+
+
+PCOMMAND_ENTRY
+AddGroupCommand(
+ PCOMMAND_GROUP pGroup,
+ LPCWSTR pwszCmdToken,
+ PFN_HANDLE_CMD pfnCmdHandler,
+ DWORD dwShortCmdHelpToken,
+ DWORD dwCmdHlpToken,
+ DWORD dwFlags)
+{
+ PCOMMAND_ENTRY pEntry;
+
+ if (pfnCmdHandler == NULL)
+ return NULL;
+
+ /* Allocate the entry */
+ pEntry = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(COMMAND_ENTRY));
+ if (pEntry == NULL)
+ return NULL;
+
+ pEntry->pwszCmdToken = HeapAlloc(GetProcessHeap(),
+ HEAP_ZERO_MEMORY,
+ (wcslen(pwszCmdToken) + 1) * sizeof(WCHAR));
+ if (pEntry->pwszCmdToken == NULL)
+ {
+ HeapFree(GetProcessHeap(), 0, pEntry);
+ return NULL;
+ }
+
+ wcscpy((LPWSTR)pEntry->pwszCmdToken, pwszCmdToken);
+
+ pEntry->pfnCmdHandler = pfnCmdHandler;
+ pEntry->dwShortCmdHelpToken = dwShortCmdHelpToken;
+ pEntry->dwCmdHlpToken = dwCmdHlpToken;
+ pEntry->dwFlags = dwFlags;
+
+ if (pGroup->pCommandListHead == NULL && pGroup->pCommandListTail ==
NULL)
+ {
+ pGroup->pCommandListHead = pEntry;
+ pGroup->pCommandListTail = pEntry;
+ }
+ else
+ {
+ pEntry->pPrev = pGroup->pCommandListTail;
+ pGroup->pCommandListTail->pNext = pEntry;
+ pGroup->pCommandListTail = pEntry;
+ }
+
+ return pEntry;
+}
+
+
+VOID
+DeleteContext(
+ PWSTR pszName)
+{
+ /* Delete all commands */
+ /* Delete the context */
+}
+
+
+DWORD
+WINAPI
+UpCommand(
+ LPCWSTR pwszMachine,
+ LPWSTR *argv,
+ DWORD dwCurrentIndex,
+ DWORD dwArgCount,
+ DWORD dwFlags,
+ LPCVOID pvData,
+ BOOL *pbDone)
+{
+ if (pCurrentContext != pRootContext)
+ pCurrentContext = pCurrentContext->pParentContext;
+
+ return 0;
+}
+
+
+DWORD
+WINAPI
+ExitCommand(
+ LPCWSTR pwszMachine,
+ LPWSTR *argv,
+ DWORD dwCurrentIndex,
+ DWORD dwArgCount,
+ DWORD dwFlags,
+ LPCVOID pvData,
+ BOOL *pbDone)
+{
+ *pbDone = TRUE;
+ return 0;
+}
+
+
+DWORD
+WINAPI
+RemCommand(
+ LPCWSTR pwszMachine,
+ LPWSTR *argv,
+ DWORD dwCurrentIndex,
+ DWORD dwArgCount,
+ DWORD dwFlags,
+ LPCVOID pvData,
+ BOOL *pbDone)
+{
+ return 0;
+}
+
+
+BOOL
+CreateRootContext(VOID)
+{
+ PCOMMAND_GROUP pGroup;
+
+ pRootContext = AddContext(NULL, NULL, NULL);
+ DPRINT1("pRootContext: %p\n", pRootContext);
+ if (pRootContext == NULL)
+ return FALSE;
+
+ pRootContext->hModule = GetModuleHandle(NULL);
+
+ AddContextCommand(pRootContext, L"..", UpCommand, IDS_HLP_UP,
IDS_HLP_UP_EX, 0);
+ AddContextCommand(pRootContext, L"?", HelpCommand, IDS_HLP_HELP,
IDS_HLP_HELP_EX, 0);
+ AddContextCommand(pRootContext, L"bye", ExitCommand, IDS_HLP_EXIT,
IDS_HLP_EXIT_EX, 0);
+ AddContextCommand(pRootContext, L"exit", ExitCommand, IDS_HLP_EXIT,
IDS_HLP_EXIT_EX, 0);
+ AddContextCommand(pRootContext, L"help", HelpCommand, IDS_HLP_HELP,
IDS_HLP_HELP_EX, 0);
+ AddContextCommand(pRootContext, L"quit", ExitCommand, IDS_HLP_EXIT,
IDS_HLP_EXIT_EX, 0);
+
+ pGroup = AddCommandGroup(pRootContext, L"add", IDS_HLP_GROUP_ADD, 0);
+ if (pGroup)
+ {
+ AddGroupCommand(pGroup, L"helper", AddHelperCommand,
IDS_HLP_ADD_HELPER, IDS_HLP_ADD_HELPER_EX, 0);
+ }
+
+ pGroup = AddCommandGroup(pRootContext, L"delete", IDS_HLP_GROUP_DELETE,
0);
+ if (pGroup)
+ {
+ AddGroupCommand(pGroup, L"helper", DeleteHelperCommand,
IDS_HLP_DEL_HELPER, IDS_HLP_DEL_HELPER_EX, 0);
+ }
+
+ pGroup = AddCommandGroup(pRootContext, L"show", IDS_HLP_GROUP_SHOW, 0);
+ if (pGroup)
+ {
+ AddGroupCommand(pGroup, L"helper", ShowHelperCommand,
IDS_HLP_SHOW_HELPER, IDS_HLP_SHOW_HELPER_EX, 0);
+ }
+
+ pCurrentContext = pRootContext;
+
+ return TRUE;
+}
+
+
+DWORD
+WINAPI
+RegisterContext(
+ _In_ const NS_CONTEXT_ATTRIBUTES *pChildContext)
+{
+ PCONTEXT_ENTRY pContext;
+ DWORD i;
+
+ DPRINT1("RegisterContext(%p)\n", pChildContext);
+ if (pChildContext == NULL)
+ {
+ DPRINT1("Invalid child context!\n");
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ if ((pChildContext->pwszContext == NULL) ||
+ (wcslen(pChildContext->pwszContext) == 0) ||
+ (wcschr(pChildContext->pwszContext, L' ') != 0) ||
+ (wcschr(pChildContext->pwszContext, L'=') != 0))
+ {
+ DPRINT1("Invalid context name!\n");
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ DPRINT1("Name: %S\n", pChildContext->pwszContext);
+
+ pContext = AddContext(pRootContext, pChildContext->pwszContext,
(GUID*)&pChildContext->guidHelper);
+ if (pContext != NULL)
+ {
+ for (i = 0; i < pChildContext->ulNumTopCmds; i++)
+ {
+ AddContextCommand(pContext,
+ pChildContext->pTopCmds[i].pwszCmdToken,
+ pChildContext->pTopCmds[i].pfnCmdHandler,
+ pChildContext->pTopCmds[i].dwShortCmdHelpToken,
+ pChildContext->pTopCmds[i].dwCmdHlpToken,
+ pChildContext->pTopCmds[i].dwFlags);
+ }
+
+ /* Add command groups */
+ for (i = 0; i < pChildContext->ulNumGroups; i++)
+ {
+ AddCommandGroup(pContext,
+ pChildContext->pCmdGroups[i].pwszCmdGroupToken,
+ pChildContext->pCmdGroups[i].dwShortCmdHelpToken,
+ pChildContext->pCmdGroups[i].dwFlags);
+ }
+ }
+
+ return ERROR_SUCCESS;
+}
diff --git a/base/applications/network/netsh/help.c
b/base/applications/network/netsh/help.c
new file mode 100644
index 00000000000..a446e1ec10c
--- /dev/null
+++ b/base/applications/network/netsh/help.c
@@ -0,0 +1,152 @@
+/*
+ * PROJECT: ReactOS NetSh
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Network Shell builtin help command and support functions
+ * COPYRIGHT: Copyright 2023 Eric Kohl <eric.kohl(a)reactos.org>
+ */
+
+/* INCLUDES *******************************************************************/
+
+#include "precomp.h"
+
+#define NDEBUG
+#include <debug.h>
+
+/* FUNCTIONS ******************************************************************/
+
+static
+VOID
+GetContextFullName(
+ _In_ PCONTEXT_ENTRY pContext,
+ _Inout_ LPWSTR pszBuffer,
+ _In_ DWORD cchLength)
+{
+ if (pContext->pParentContext != NULL)
+ {
+ GetContextFullName(pContext->pParentContext, pszBuffer, cchLength);
+ wcscat(pszBuffer, L" ");
+ wcscat(pszBuffer, pContext->pszContextName);
+ }
+ else
+ {
+ wcscpy(pszBuffer, L"netsh");
+ }
+}
+
+
+static
+VOID
+HelpContext(
+ PCONTEXT_ENTRY pContext)
+{
+ PCONTEXT_ENTRY pSubContext;
+ PCOMMAND_ENTRY pCommand;
+ PCOMMAND_GROUP pGroup;
+ WCHAR szBuffer[80];
+
+ if (pContext != pRootContext)
+ HelpContext(pContext->pParentContext);
+
+ if (pContext == pCurrentContext)
+ {
+ ConPrintf(StdOut, L"\nCommands in this context:\n");
+ }
+ else if (pContext == pRootContext)
+ {
+ ConPrintf(StdOut, L"\nCommands in the netsh-context:\n");
+ }
+ else
+ {
+ GetContextFullName(pContext, szBuffer, 80);
+ ConPrintf(StdOut, L"\nCommands in the %s-context:\n", szBuffer);
+ }
+
+ pCommand = pContext->pCommandListHead;
+ while (pCommand != NULL)
+ {
+ if (LoadStringW(pContext->hModule, pCommand->dwShortCmdHelpToken, szBuffer,
80) == 0)
+ szBuffer[0] = UNICODE_NULL;
+ ConPrintf(StdOut, L"%-15s - %s\n", pCommand->pwszCmdToken,
szBuffer);
+ pCommand = pCommand->pNext;
+ }
+
+ pGroup = pContext->pGroupListHead;
+ while (pGroup != NULL)
+ {
+ if (LoadStringW(pContext->hModule, pGroup->dwShortCmdHelpToken, szBuffer,
80) == 0)
+ szBuffer[0] = UNICODE_NULL;
+ ConPrintf(StdOut, L"%-15s - %s\n", pGroup->pwszCmdGroupToken,
szBuffer);
+ pGroup = pGroup->pNext;
+ }
+
+ pSubContext = pContext->pSubContextHead;
+ while (pSubContext != NULL)
+ {
+ GetContextFullName(pSubContext, szBuffer, 80);
+ ConPrintf(StdOut, L"%-15s - Changes to the \"%s\"
context.\n", pSubContext->pszContextName, szBuffer);
+ pSubContext = pSubContext->pNext;
+ }
+}
+
+
+VOID
+HelpGroup(
+ PCOMMAND_GROUP pGroup)
+{
+ PCOMMAND_ENTRY pCommand;
+ WCHAR szBuffer[64];
+
+ ConResPrintf(StdOut, IDS_HELP_HEADER);
+
+ ConPrintf(StdOut, L"\nCommands in this context:\n");
+
+ pCommand = pGroup->pCommandListHead;
+ while (pCommand != NULL)
+ {
+ swprintf(szBuffer, L"%s %s", pGroup->pwszCmdGroupToken,
pCommand->pwszCmdToken);
+ ConPrintf(StdOut, L"%-15s - ", szBuffer);
+ ConResPuts(StdOut, pCommand->dwShortCmdHelpToken);
+ pCommand = pCommand->pNext;
+ }
+}
+
+
+DWORD
+WINAPI
+HelpCommand(
+ LPCWSTR pwszMachine,
+ LPWSTR *ppwcArguments,
+ DWORD dwCurrentIndex,
+ DWORD dwArgCount,
+ DWORD dwFlags,
+ LPCVOID pvData,
+ BOOL *pbDone)
+{
+ PCONTEXT_ENTRY pContext;
+
+ ConResPrintf(StdOut, IDS_HELP_HEADER);
+
+ pContext = pCurrentContext;
+ if (pContext == NULL)
+ {
+ DPRINT1("HelpCommand: invalid context %p\n", pContext);
+ return 1;
+ }
+
+ HelpContext(pContext);
+
+ if (pCurrentContext->pSubContextHead != NULL)
+ {
+ ConResPrintf(StdOut, IDS_SUBCONTEXT_HEADER);
+ pContext = pCurrentContext->pSubContextHead;
+ while (pContext != NULL)
+ {
+ ConPrintf(StdOut, L" %s", pContext->pszContextName);
+ pContext = pContext->pNext;
+ }
+ ConPuts(StdOut, L"\n");
+ }
+ ConPuts(StdOut, L"\n");
+
+ return ERROR_SUCCESS;
+}
diff --git a/base/applications/network/netsh/helper.c
b/base/applications/network/netsh/helper.c
new file mode 100644
index 00000000000..2054a647019
--- /dev/null
+++ b/base/applications/network/netsh/helper.c
@@ -0,0 +1,593 @@
+/*
+ * PROJECT: ReactOS NetSh
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Network Shell helper dll management and support functions
+ * COPYRIGHT: Copyright 2023 Eric Kohl <eric.kohl(a)reactos.org>
+ */
+
+/* INCLUDES *******************************************************************/
+
+#include "precomp.h"
+
+#define NDEBUG
+#include <debug.h>
+
+/* GLOBALS ********************************************************************/
+
+PDLL_LIST_ENTRY pDllListHead = NULL;
+PDLL_LIST_ENTRY pDllListTail = NULL;
+
+PHELPER_ENTRY pHelperListHead = NULL;
+PHELPER_ENTRY pHelperListTail = NULL;
+
+PDLL_LIST_ENTRY pCurrentDll = NULL;
+
+/* FUNCTIONS ******************************************************************/
+
+static
+VOID
+StartHelpers(VOID)
+{
+ PHELPER_ENTRY pHelper;
+ DWORD dwError;
+
+ pHelper = pHelperListHead;
+ while (pHelper != NULL)
+ {
+ if (pHelper->bStarted == FALSE)
+ {
+ if (pHelper->Attributes.pfnStart)
+ {
+ dwError = pHelper->Attributes.pfnStart(NULL, 0);
+ if (dwError == ERROR_SUCCESS)
+ pHelper->bStarted = TRUE;
+ }
+ }
+
+ pHelper = pHelper->pNext;
+ }
+}
+
+
+static
+VOID
+RegisterHelperDll(
+ _In_ PDLL_LIST_ENTRY pEntry)
+{
+ PWSTR pszValueName = NULL;
+ HKEY hKey;
+ DWORD dwError;
+
+ dwError = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
+ REG_NETSH_PATH,
+ 0,
+ NULL,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE,
+ NULL,
+ &hKey,
+ NULL);
+ if (dwError == ERROR_SUCCESS)
+ {
+ RegSetValueExW(hKey,
+ pEntry->pszValueName,
+ 0,
+ REG_SZ,
+ (PBYTE)pEntry->pszDllName,
+ (wcslen(pEntry->pszDllName) + 1) * sizeof(WCHAR));
+
+ RegCloseKey(hKey);
+ }
+
+ HeapFree(GetProcessHeap(), 0, pszValueName);
+}
+
+
+static
+VOID
+FreeHelperDll(
+ _In_ PDLL_LIST_ENTRY pEntry)
+{
+ if (pEntry->hModule)
+ FreeLibrary(pEntry->hModule);
+
+ if (pEntry->pszValueName)
+ HeapFree(GetProcessHeap(), 0, pEntry->pszValueName);
+
+ if (pEntry->pszShortName)
+ HeapFree(GetProcessHeap(), 0, pEntry->pszShortName);
+
+ if (pEntry->pszDllName)
+ HeapFree(GetProcessHeap(), 0, pEntry->pszDllName);
+
+ HeapFree(GetProcessHeap(), 0, pEntry);
+}
+
+
+static
+DWORD
+LoadHelperDll(
+ _In_ PWSTR pszDllName,
+ _In_ BOOL bRegister)
+{
+ PNS_DLL_INIT_FN pInitHelperDll;
+ PDLL_LIST_ENTRY pEntry;
+ PWSTR pszStart, pszEnd;
+ BOOL bInserted = FALSE;
+ DWORD dwError;
+
+ pEntry = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DLL_LIST_ENTRY));
+ if (pEntry == NULL)
+ {
+ return ERROR_OUTOFMEMORY;
+ }
+
+ pEntry->pszDllName = HeapAlloc(GetProcessHeap(),
+ HEAP_ZERO_MEMORY,
+ (wcslen(pszDllName) + 1) * sizeof(WCHAR));
+ if (pEntry->pszDllName == NULL)
+ {
+ dwError = ERROR_OUTOFMEMORY;
+ goto done;
+ }
+
+ wcscpy(pEntry->pszDllName, pszDllName);
+
+ pszStart = wcsrchr(pszDllName, L'\\');
+ if (pszStart == NULL)
+ pszStart = pszDllName;
+
+ pEntry->pszShortName = HeapAlloc(GetProcessHeap(),
+ HEAP_ZERO_MEMORY,
+ (wcslen(pszStart) + 1) * sizeof(WCHAR));
+ if (pEntry->pszShortName == NULL)
+ {
+ dwError = ERROR_OUTOFMEMORY;
+ goto done;
+ }
+
+ wcscpy(pEntry->pszShortName, pszStart);
+
+ pEntry->pszValueName = HeapAlloc(GetProcessHeap(),
+ HEAP_ZERO_MEMORY,
+ (wcslen(pEntry->pszShortName) + 1) *
sizeof(WCHAR));
+ if (pEntry->pszValueName == NULL)
+ {
+ dwError = ERROR_OUTOFMEMORY;
+ goto done;
+ }
+
+ wcscpy(pEntry->pszValueName, pEntry->pszShortName);
+
+ pszEnd = wcsrchr(pEntry->pszValueName, L'.');
+ if (pszEnd != NULL)
+ *pszEnd = UNICODE_NULL;
+
+ if (pDllListTail == NULL)
+ {
+ pEntry->pPrev = NULL;
+ pEntry->pNext = NULL;
+ pDllListHead = pEntry;
+ pDllListTail = pEntry;
+ }
+ else
+ {
+ pEntry->pPrev = NULL;
+ pEntry->pNext = pDllListHead;
+ pDllListHead->pPrev = pEntry;
+ pDllListHead = pEntry;
+ }
+
+ bInserted = TRUE;
+
+ pEntry->hModule = LoadLibraryW(pEntry->pszDllName);
+ if (pEntry->hModule == NULL)
+ {
+ dwError = GetLastError();
+ DPRINT1("Could not load the helper dll %S (Error: %lu)\n",
pEntry->pszDllName, dwError);
+ goto done;
+ }
+
+ pInitHelperDll = (PNS_DLL_INIT_FN)GetProcAddress(pEntry->hModule,
"InitHelperDll");
+ if (pInitHelperDll == NULL)
+ {
+ dwError = GetLastError();
+ DPRINT1("Could not find 'InitHelperDll' (Error: %lu)\n",
dwError);
+ goto done;
+ }
+
+ pCurrentDll = pEntry;
+ dwError = pInitHelperDll(5, NULL);
+ pCurrentDll = NULL;
+
+ DPRINT1("InitHelperDll returned %lu\n", dwError);
+ if (dwError != ERROR_SUCCESS)
+ {
+ DPRINT1("Call to InitHelperDll failed (Error: %lu)\n", dwError);
+ goto done;
+ }
+
+// if (pEntry->Attributes.pfnStart)
+// pEntry->Attributes.pfnStart(NULL, 0);
+
+ if (bRegister)
+ RegisterHelperDll(pEntry);
+
+done:
+ if (dwError != ERROR_SUCCESS)
+ {
+ if (bInserted)
+ {
+ if (pEntry->pPrev != NULL)
+ pEntry->pPrev->pNext = pEntry->pNext;
+ if (pEntry->pNext != NULL)
+ pEntry->pNext->pPrev = pEntry->pPrev;
+ if (pDllListTail == pEntry)
+ pDllListTail = pEntry->pPrev;
+ if (pDllListHead == pEntry)
+ pDllListHead = pEntry->pNext;
+ pEntry->pPrev = NULL;
+ pEntry->pNext = NULL;
+ }
+
+ FreeHelperDll(pEntry);
+ }
+
+ return dwError;
+}
+
+
+VOID
+LoadHelpers(VOID)
+{
+ PWSTR pszNameBuffer = NULL;
+ PWSTR pszValueBuffer = NULL;
+ HKEY hKey;
+ DWORD dwValueCount, dwMaxNameLength, dwMaxValueLength;
+ DWORD dwNameLength, dwValueLength, dwType;
+ DWORD dwIndex, dwError;
+
+ DPRINT1("LoadHelpers()\n");
+
+ dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+ REG_NETSH_PATH,
+ 0,
+ KEY_READ,
+ &hKey);
+ if (dwError != ERROR_SUCCESS)
+ return;
+
+ dwError = RegQueryInfoKeyW(hKey,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &dwValueCount,
+ &dwMaxNameLength,
+ &dwMaxValueLength,
+ NULL,
+ NULL);
+ if (dwError != ERROR_SUCCESS)
+ goto done;
+
+ pszNameBuffer = HeapAlloc(GetProcessHeap(), 0,
+ (dwMaxNameLength + 1) * sizeof(WCHAR));
+ if (pszNameBuffer == NULL)
+ goto done;
+
+ pszValueBuffer = HeapAlloc(GetProcessHeap(), 0,
+ dwMaxValueLength + sizeof(WCHAR));
+ if (pszValueBuffer == NULL)
+ goto done;
+
+ for (dwIndex = 0; dwIndex < dwValueCount; dwIndex++)
+ {
+ dwNameLength = dwMaxNameLength + 1;
+ dwValueLength = dwMaxValueLength + sizeof(WCHAR);
+ dwError = RegEnumValueW(hKey,
+ dwIndex,
+ pszNameBuffer,
+ &dwNameLength,
+ NULL,
+ &dwType,
+ (PBYTE)pszValueBuffer,
+ &dwValueLength);
+ if (dwError != ERROR_SUCCESS)
+ break;
+
+ DPRINT1("Dll: %S --> %S %lu\n", pszNameBuffer, pszValueBuffer,
dwError);
+ LoadHelperDll(pszValueBuffer, FALSE);
+ }
+
+done:
+ if (pszValueBuffer)
+ HeapFree(GetProcessHeap(), 0, pszValueBuffer);
+
+ if (pszNameBuffer)
+ HeapFree(GetProcessHeap(), 0, pszNameBuffer);
+
+ RegCloseKey(hKey);
+
+ StartHelpers();
+}
+
+
+VOID
+UnloadHelpers(VOID)
+{
+ PDLL_LIST_ENTRY pEntry;
+
+ while (pDllListHead != NULL)
+ {
+ pEntry = pDllListHead;
+ pDllListHead = pEntry->pNext;
+
+// if (pEntry->Attributes.pfnStop)
+// pEntry->Attributes.pfnStop(0);
+
+ FreeHelperDll(pEntry);
+ }
+
+ pDllListTail = NULL;
+}
+
+
+PHELPER_ENTRY
+FindHelper(
+ _In_ const GUID *pguidHelper)
+{
+ PHELPER_ENTRY pHelper;
+
+ pHelper = pHelperListHead;
+ while (pHelper != NULL)
+ {
+ if (IsEqualGUID(pguidHelper, &pHelper->Attributes.guidHelper))
+ return pHelper;
+
+ pHelper = pHelper->pNext;
+ }
+
+ return NULL;
+}
+
+
+DWORD
+WINAPI
+RegisterHelper(
+ _In_ const GUID *pguidParentHelper,
+ _In_ const NS_HELPER_ATTRIBUTES *pHelperAttributes)
+{
+ PHELPER_ENTRY pHelper = NULL, pParentHelper;
+ DWORD dwError = ERROR_SUCCESS;
+
+ DPRINT("RegisterHelper(%p %p)\n", pguidParentHelper, pHelperAttributes);
+
+ if (FindHelper(&pHelperAttributes->guidHelper) != NULL)
+ {
+ DPRINT1("The Helper has already been registered!\n");
+ return 1;
+ }
+
+ pHelper = (PHELPER_ENTRY)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
sizeof(HELPER_ENTRY));
+ if (pHelper == NULL)
+ {
+ dwError = ERROR_OUTOFMEMORY;
+ goto done;
+ }
+
+ CopyMemory(&pHelper->Attributes, pHelperAttributes,
sizeof(NS_HELPER_ATTRIBUTES));
+ pHelper->pDllEntry = pCurrentDll;
+ DPRINT("pHelper->pDllEntry: %p\n", pHelper->pDllEntry);
+
+ if (pguidParentHelper == NULL)
+ {
+ if (pHelperListTail == NULL)
+ {
+ pHelperListHead = pHelper;
+ pHelperListTail = pHelper;
+ }
+ else
+ {
+ pHelper->pNext = pHelperListHead;
+ pHelperListHead->pPrev = pHelper;
+ pHelperListHead = pHelper;
+ }
+ }
+ else
+ {
+ pParentHelper = FindHelper(&pHelperAttributes->guidHelper);
+ if (pParentHelper == NULL)
+ return ERROR_INVALID_PARAMETER;
+
+ if (pParentHelper->pSubHelperHead == NULL &&
pParentHelper->pSubHelperTail == NULL)
+ {
+ pParentHelper->pSubHelperHead = pHelper;
+ pParentHelper->pSubHelperTail = pHelper;
+ }
+ else
+ {
+ pHelper->pPrev = pParentHelper->pSubHelperTail;
+ pParentHelper->pSubHelperTail->pNext = pHelper;
+ pParentHelper->pSubHelperTail = pHelper;
+ }
+ }
+
+done:
+
+ return dwError;
+}
+
+
+DWORD
+WINAPI
+AddHelperCommand(
+ LPCWSTR pwszMachine,
+ LPWSTR *ppwcArguments,
+ DWORD dwCurrentIndex,
+ DWORD dwArgCount,
+ DWORD dwFlags,
+ LPCVOID pvData,
+ BOOL *pbDone)
+{
+ DWORD dwError = ERROR_SUCCESS;
+
+ DPRINT("AddHelperCommand()\n");
+
+ if (dwArgCount == 2)
+ {
+// ConResPrintf(StdErr, IDS_INVALID_SYNTAX);
+// ConResPrintf(StdErr, IDS_HLP_ADD_HELPER_EX);
+ return 1;
+ }
+
+ dwError = LoadHelperDll(ppwcArguments[2], TRUE);
+ if (dwError != ERROR_SUCCESS)
+ return dwError;
+
+ StartHelpers();
+
+ return ERROR_SUCCESS;
+}
+
+
+DWORD
+WINAPI
+DeleteHelperCommand(
+ LPCWSTR pwszMachine,
+ LPWSTR *ppwcArguments,
+ DWORD dwCurrentIndex,
+ DWORD dwArgCount,
+ DWORD dwFlags,
+ LPCVOID pvData,
+ BOOL *pbDone)
+{
+ PDLL_LIST_ENTRY pEntry;
+ HKEY hKey;
+ DWORD dwError;
+
+ DPRINT("DeleteHelper()\n");
+
+ if (dwArgCount == 2)
+ {
+// ConResPrintf(StdErr, IDS_INVALID_SYNTAX);
+// ConResPrintf(StdErr, IDS_HLP_DEL_HELPER_EX);
+ return 1;
+ }
+
+ pEntry = pDllListHead;
+ while (pEntry != NULL)
+ {
+ if (wcscmp(pEntry->pszShortName, ppwcArguments[2]) == 0)
+ {
+ DPRINT1("remove %S\n", pEntry->pszShortName);
+
+ if (pEntry->pPrev != NULL)
+ pEntry->pPrev->pNext = pEntry->pNext;
+ if (pEntry->pNext != NULL)
+ pEntry->pNext->pPrev = pEntry->pPrev;
+ if (pDllListTail == pEntry)
+ pDllListTail = pEntry->pPrev;
+ if (pDllListHead == pEntry)
+ pDllListHead = pEntry->pNext;
+ pEntry->pPrev = NULL;
+ pEntry->pNext = NULL;
+
+ dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+ REG_NETSH_PATH,
+ 0,
+ KEY_WRITE,
+ &hKey);
+ if (dwError == ERROR_SUCCESS)
+ {
+ RegDeleteValue(hKey, pEntry->pszValueName);
+ RegCloseKey(hKey);
+ }
+
+ FreeHelperDll(pEntry);
+
+ return 1;
+ }
+
+ pEntry = pEntry->pNext;
+ }
+
+ return ERROR_SUCCESS;
+}
+
+
+static
+VOID
+PrintSubContext(
+ _In_ PCONTEXT_ENTRY pParentContext,
+ _In_ DWORD dwLevel)
+{
+ PCONTEXT_ENTRY pContext;
+ PHELPER_ENTRY pHelper;
+ WCHAR szPrefix[22];
+ DWORD i;
+
+ if (pParentContext == NULL)
+ return;
+
+ pContext = pParentContext->pSubContextHead;
+ while (pContext != NULL)
+ {
+ pHelper = FindHelper(&pContext->Guid);
+ if (pHelper != NULL)
+ {
+ if (dwLevel > 10)
+ dwLevel = 10;
+
+ for (i = 0; i < dwLevel * 2; i++)
+ szPrefix[i] = L' ';
+ szPrefix[i] = UNICODE_NULL;
+
+ ConPrintf(StdOut, L"{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}
%-16s %s%s\n",
+ pHelper->Attributes.guidHelper.Data1,
+ pHelper->Attributes.guidHelper.Data2,
+ pHelper->Attributes.guidHelper.Data3,
+ pHelper->Attributes.guidHelper.Data4[0],
+ pHelper->Attributes.guidHelper.Data4[1],
+ pHelper->Attributes.guidHelper.Data4[2],
+ pHelper->Attributes.guidHelper.Data4[3],
+ pHelper->Attributes.guidHelper.Data4[4],
+ pHelper->Attributes.guidHelper.Data4[5],
+ pHelper->Attributes.guidHelper.Data4[6],
+ pHelper->Attributes.guidHelper.Data4[7],
+ pHelper->pDllEntry->pszShortName,
+ szPrefix,
+ pContext->pszContextName);
+ }
+
+ PrintSubContext(pContext, dwLevel + 1);
+
+ pContext = pContext->pNext;
+ }
+}
+
+
+DWORD
+WINAPI
+ShowHelperCommand(
+ LPCWSTR pwszMachine,
+ LPWSTR *ppwcArguments,
+ DWORD dwCurrentIndex,
+ DWORD dwArgCount,
+ DWORD dwFlags,
+ LPCVOID pvData,
+ BOOL *pbDone)
+{
+ DPRINT("ShowHelperCommand()\n");
+
+ ConPrintf(StdOut, L"Helper GUID DLL Name
Command\n");
+ ConPrintf(StdOut, L"-------------------------------------- ----------------
--------\n");
+
+ if (pRootContext == NULL)
+ return ERROR_SUCCESS;
+
+ PrintSubContext(pRootContext, 0);
+
+ return ERROR_SUCCESS;
+}
diff --git a/base/applications/network/netsh/interpreter.c
b/base/applications/network/netsh/interpreter.c
new file mode 100644
index 00000000000..87c5c2fc2fe
--- /dev/null
+++ b/base/applications/network/netsh/interpreter.c
@@ -0,0 +1,238 @@
+/*
+ * PROJECT: ReactOS NetSh
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Network Shell command interpreter
+ * COPYRIGHT: Copyright 2023 Eric Kohl <eric.kohl(a)reactos.org>
+ */
+
+/* INCLUDES *******************************************************************/
+
+#include "precomp.h"
+
+#define NDEBUG
+#include <debug.h>
+
+/* FUNCTIONS *****************************************************************/
+
+BOOL
+InterpretCommand(
+ _In_ LPWSTR *argv,
+ _In_ DWORD dwArgCount)
+{
+ PCONTEXT_ENTRY pContext, pSubContext;
+ PCOMMAND_ENTRY pCommand;
+ PCOMMAND_GROUP pGroup;
+ BOOL bDone = FALSE;
+ DWORD dwHelpLevel = 0;
+ DWORD dwError = ERROR_SUCCESS;
+
+ /* If no args provided */
+ if (dwArgCount == 0)
+ return TRUE;
+
+ if (pCurrentContext == NULL)
+ {
+ DPRINT1("InterpretCmd: invalid context %p\n", pCurrentContext);
+ return FALSE;
+ }
+
+ if ((wcsicmp(argv[dwArgCount - 1], L"?") == 0) ||
+ (wcsicmp(argv[dwArgCount - 1], L"help") == 0))
+ {
+ dwHelpLevel = dwArgCount - 1;
+ }
+
+ pContext = pCurrentContext;
+
+ while (TRUE)
+ {
+ pCommand = pContext->pCommandListHead;
+ while (pCommand != NULL)
+ {
+ if (wcsicmp(argv[0], pCommand->pwszCmdToken) == 0)
+ {
+ if (dwHelpLevel == 1)
+ {
+ ConResPrintf(StdOut, pCommand->dwCmdHlpToken);
+ return TRUE;
+ }
+ else
+ {
+ dwError = pCommand->pfnCmdHandler(NULL, argv, 0, dwArgCount, 0,
NULL, &bDone);
+ if (dwError != ERROR_SUCCESS)
+ {
+ ConPrintf(StdOut, L"Error: %lu\n\n");
+ ConResPrintf(StdOut, pCommand->dwCmdHlpToken);
+ }
+ return !bDone;
+ }
+ }
+
+ pCommand = pCommand->pNext;
+ }
+
+ pGroup = pContext->pGroupListHead;
+ while (pGroup != NULL)
+ {
+ if (wcsicmp(argv[0], pGroup->pwszCmdGroupToken) == 0)
+ {
+ if (dwHelpLevel == 1)
+ {
+ HelpGroup(pGroup);
+ return TRUE;
+ }
+
+ pCommand = pGroup->pCommandListHead;
+ while (pCommand != NULL)
+ {
+ if ((dwArgCount > 1) && (wcsicmp(argv[1],
pCommand->pwszCmdToken) == 0))
+ {
+ if (dwHelpLevel == 2)
+ {
+ ConResPrintf(StdOut, pCommand->dwCmdHlpToken);
+ return TRUE;
+ }
+ else
+ {
+ dwError = pCommand->pfnCmdHandler(NULL, argv, 1,
dwArgCount, 0, NULL, &bDone);
+ if (dwError != ERROR_SUCCESS)
+ {
+ ConPrintf(StdOut, L"Error: %lu\n\n");
+ ConResPrintf(StdOut, pCommand->dwCmdHlpToken);
+ return TRUE;
+ }
+ return !bDone;
+ }
+ }
+
+ pCommand = pCommand->pNext;
+ }
+
+ HelpGroup(pGroup);
+ return TRUE;
+ }
+
+ pGroup = pGroup->pNext;
+ }
+
+ if (pContext == pCurrentContext)
+ {
+ pSubContext = pContext->pSubContextHead;
+ while (pSubContext != NULL)
+ {
+ if (wcsicmp(argv[0], pSubContext->pszContextName) == 0)
+ {
+ pCurrentContext = pSubContext;
+ return TRUE;
+ }
+
+ pSubContext = pSubContext->pNext;
+ }
+ }
+
+ if (pContext == pRootContext)
+ break;
+
+ pContext = pContext->pParentContext;
+ }
+
+ ConResPrintf(StdErr, IDS_INVALID_COMMAND, argv[0]);
+
+ return TRUE;
+}
+
+
+/*
+ * InterpretScript(char *line):
+ * The main function used for when reading commands from scripts.
+ */
+BOOL
+InterpretScript(
+ _In_ LPWSTR pszInputLine)
+{
+ LPWSTR args_vector[MAX_ARGS_COUNT];
+ DWORD dwArgCount = 0;
+ BOOL bWhiteSpace = TRUE;
+ LPWSTR ptr;
+
+ memset(args_vector, 0, sizeof(args_vector));
+
+ ptr = pszInputLine;
+ while (*ptr != 0)
+ {
+ if (iswspace(*ptr) || *ptr == L'\n')
+ {
+ *ptr = 0;
+ bWhiteSpace = TRUE;
+ }
+ else
+ {
+ if ((bWhiteSpace != FALSE) && (dwArgCount < MAX_ARGS_COUNT))
+ {
+ args_vector[dwArgCount] = ptr;
+ dwArgCount++;
+ }
+
+ bWhiteSpace = FALSE;
+ }
+
+ ptr++;
+ }
+
+ /* sends the string to find the command */
+ return InterpretCommand(args_vector, dwArgCount);
+}
+
+
+VOID
+InterpretInteractive(VOID)
+{
+ WCHAR input_line[MAX_STRING_SIZE];
+ LPWSTR args_vector[MAX_ARGS_COUNT];
+ DWORD dwArgCount = 0;
+ BOOL bWhiteSpace = TRUE;
+ BOOL bRun = TRUE;
+ LPWSTR ptr;
+
+ while (bRun != FALSE)
+ {
+ dwArgCount = 0;
+ memset(args_vector, 0, sizeof(args_vector));
+
+ /* Shown just before the input where the user places commands */
+// ConResPuts(StdOut, IDS_APP_PROMPT);
+ ConPuts(StdOut, L"netsh");
+ if (pCurrentContext != pRootContext)
+ {
+ ConPuts(StdOut, L" ");
+ ConPuts(StdOut, pCurrentContext->pszContextName);
+ }
+ ConPuts(StdOut, L">");
+
+ /* Get input from the user. */
+ fgetws(input_line, MAX_STRING_SIZE, stdin);
+
+ ptr = input_line;
+ while (*ptr != 0)
+ {
+ if (iswspace(*ptr) || *ptr == L'\n')
+ {
+ *ptr = 0;
+ bWhiteSpace = TRUE;
+ }
+ else
+ {
+ if ((bWhiteSpace != FALSE) && (dwArgCount < MAX_ARGS_COUNT))
+ {
+ args_vector[dwArgCount] = ptr;
+ dwArgCount++;
+ }
+ bWhiteSpace = FALSE;
+ }
+ ptr++;
+ }
+
+ /* Send the string to find the command */
+ bRun = InterpretCommand(args_vector, dwArgCount);
+ }
+}
diff --git a/base/applications/network/netsh/lang/en-US.rc
b/base/applications/network/netsh/lang/en-US.rc
new file mode 100644
index 00000000000..1fd6654e247
--- /dev/null
+++ b/base/applications/network/netsh/lang/en-US.rc
@@ -0,0 +1,36 @@
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+/* Basic application information */
+STRINGTABLE
+BEGIN
+ IDS_APP_USAGE "\nUsage: netsh [-a AliasFile] [-c Context] [-r RemoteMachine] \
+\n [Command | -f ScriptFile]\n"
+ IDS_INVALID_COMMAND "The following command was not found: %ls.\n"
+ IDS_OPEN_FAILED "The file %ls could not be openend.\n"
+ IDS_INVALID_SYNTAX "The syntax supplied for this command is not valid. Check
help for the correct syntax.\n\n"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_HELP_HEADER "\nThe following commands are available:\n"
+ IDS_SUBCONTEXT_HEADER "\nThe following sub-contexts are available:\n"
+ IDS_HLP_UP "Goes up one context level."
+ IDS_HLP_UP_EX "Syntax: ..\n\n Goes up one context
level.\n\n"
+ IDS_HLP_EXIT "Exits the program."
+ IDS_HLP_EXIT_EX "Syntax: exit\n\n Exits the program.\n\n"
+ IDS_HLP_HELP "Displays a list of commands."
+ IDS_HLP_HELP_EX "Syntax: help\n\n Displays a list of
commands.\n\n"
+
+ IDS_HLP_ADD_HELPER "Installs a helper DLL."
+ IDS_HLP_ADD_HELPER_EX "Syntax: add helper <dll file name>\n\n
Installs the specified helper DLL in netsh.\n\n"
+
+ IDS_HLP_DEL_HELPER "Removes a helper DLL."
+ IDS_HLP_DEL_HELPER_EX "Syntax: delete helper <dll file name>\n\n
Removes the specified helper DLL from netsh.\n\n"
+
+ IDS_HLP_SHOW_HELPER "Lists all the top-level helpers."
+ IDS_HLP_SHOW_HELPER_EX "Syntax: show helper\n\n Lists all the top-level
helpers.\n\n"
+
+ IDS_HLP_GROUP_ADD "Adds a configuration entry to a list of entries."
+ IDS_HLP_GROUP_DELETE "Deletes a configuration entry from a list of
entries."
+ IDS_HLP_GROUP_SHOW "Displays information."
+END
diff --git a/base/applications/network/netsh/netsh.c
b/base/applications/network/netsh/netsh.c
index bf938698d3c..7c0a33b669b 100644
--- a/base/applications/network/netsh/netsh.c
+++ b/base/applications/network/netsh/netsh.c
@@ -1,33 +1,254 @@
/*
- * Copyright 2010 Hans Leidekker for CodeWeavers
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ * PROJECT: ReactOS NetSh
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Network Shell main file
+ * COPYRIGHT: Copyright 2023 Eric Kohl <eric.kohl(a)reactos.org>
*/
-#include <wine/debug.h>
+/* INCLUDES *******************************************************************/
-WINE_DEFAULT_DEBUG_CHANNEL(netsh);
+#include "precomp.h"
-int wmain(int argc, WCHAR *argv[])
+#define NDEBUG
+#include <debug.h>
+
+/* FUNCTIONS ******************************************************************/
+
+BOOL
+RunScript(
+ _In_ LPCWSTR filename)
+{
+ FILE *script;
+ WCHAR tmp_string[MAX_STRING_SIZE];
+
+ /* Open the file for processing */
+ script = _wfopen(filename, L"r");
+ if (script == NULL)
+ {
+ ConResPrintf(StdErr, IDS_OPEN_FAILED, filename);
+ return FALSE;
+ }
+
+ /* Read and process the script */
+ while (fgetws(tmp_string, MAX_STRING_SIZE, script) != NULL)
+ {
+ if (InterpretScript(tmp_string) == FALSE)
+ {
+ fclose(script);
+ return FALSE;
+ }
+ }
+
+ /* Close the file */
+ fclose(script);
+
+ return TRUE;
+}
+
+/*
+ * wmain():
+ * Main entry point of the application.
+ */
+int
+wmain(
+ _In_ int argc,
+ _In_ const LPWSTR argv[])
{
- int i;
+ LPCWSTR tmpBuffer = NULL;
+ LPCWSTR pszFileName = NULL;
+ int index;
+ int result = EXIT_SUCCESS;
+
+ DPRINT("main()\n");
+
+ /* Initialize the Console Standard Streams */
+ ConInitStdStreams();
+
+ /* FIXME: Init code goes here */
+ CreateRootContext();
+ LoadHelpers();
+
+ if (argc < 2)
+ {
+ /* If there are no command arguments, then go straight to the interpreter */
+ InterpretInteractive();
+ }
+ else
+ {
+ /* If there are command arguments, then process them */
+ for (index = 1; index < argc; index++)
+ {
+ if ((argv[index][0] == '/')||
+ (argv[index][0] == '-'))
+ {
+ tmpBuffer = argv[index] + 1;
+ }
+ else
+ {
+ if (pszFileName != NULL)
+ {
+ ConResPuts(StdOut, IDS_APP_USAGE);
+ result = EXIT_FAILURE;
+ goto done;
+ }
+
+ /* Run a command from the command line */
+ if (InterpretCommand((LPWSTR*)&argv[index], argc - index) == FALSE)
+ result = EXIT_FAILURE;
+ goto done;
+ }
+
+ if (_wcsicmp(tmpBuffer, L"?") == 0)
+ {
+ /* Help option */
+ ConResPuts(StdOut, IDS_APP_USAGE);
+ result = EXIT_SUCCESS;
+ goto done;
+ }
+ else if (_wcsicmp(tmpBuffer, L"a") == 0)
+ {
+ /* Aliasfile option */
+ if ((index + 1) < argc)
+ {
+ index++;
+ ConPuts(StdOut, L"\nThe -a option is not implemented
yet\n");
+// aliasfile = argv[index];
+ }
+ else
+ {
+ ConResPuts(StdOut, IDS_APP_USAGE);
+ result = EXIT_FAILURE;
+ }
+ }
+ else if (_wcsicmp(tmpBuffer, L"c") == 0)
+ {
+ /* Context option */
+ if ((index + 1) < argc)
+ {
+ index++;
+ ConPuts(StdOut, L"\nThe -c option is not implemented
yet\n");
+// context = argv[index];
+ }
+ else
+ {
+ ConResPuts(StdOut, IDS_APP_USAGE);
+ result = EXIT_FAILURE;
+ }
+ }
+ else if (_wcsicmp(tmpBuffer, L"f") == 0)
+ {
+ /* File option */
+ if ((index + 1) < argc)
+ {
+ index++;
+ pszFileName = argv[index];
+ }
+ else
+ {
+ ConResPuts(StdOut, IDS_APP_USAGE);
+ result = EXIT_FAILURE;
+ }
+ }
+ else if (_wcsicmp(tmpBuffer, L"r") == 0)
+ {
+ /* Remote option */
+ if ((index + 1) < argc)
+ {
+ index++;
+ ConPuts(StdOut, L"\nThe -r option is not implemented
yet\n");
+// remote = argv[index];
+ }
+ else
+ {
+ ConResPuts(StdOut, IDS_APP_USAGE);
+ result = EXIT_FAILURE;
+ }
+ }
+ else
+ {
+ /* Invalid command */
+ ConResPrintf(StdOut, IDS_INVALID_COMMAND, argv[index]);
+ result = EXIT_FAILURE;
+ goto done;
+ }
+ }
+
+ /* Now we process the filename if it exists */
+ if (pszFileName != NULL)
+ {
+ if (RunScript(pszFileName) == FALSE)
+ {
+ result = EXIT_FAILURE;
+ goto done;
+ }
+ }
+ }
- WINE_FIXME("stub:");
- for (i = 0; i < argc; i++)
- WINE_FIXME(" %s", wine_dbgstr_w(argv[i]));
- WINE_FIXME("\n");
+done:
+ /* FIXME: Cleanup code goes here */
+ UnloadHelpers();
+ return result;
+}
+
+
+DWORD
+WINAPI
+MatchEnumTag(
+ _In_ HANDLE hModule,
+ _In_ LPCWSTR pwcArg,
+ _In_ DWORD dwNumArg,
+ _In_ const TOKEN_VALUE *pEnumTable,
+ _Out_ PDWORD pdwValue)
+{
+ DPRINT1("MatchEnumTag()\n");
return 0;
}
+
+BOOL
+WINAPI
+MatchToken(
+ _In_ LPCWSTR pwszUserToken,
+ _In_ LPCWSTR pwszCmdToken)
+{
+ DPRINT1("MatchToken %S %S\n", pwszUserToken, pwszCmdToken);
+ return (wcsicmp(pwszUserToken, pwszCmdToken) == 0) ? TRUE : FALSE;
+}
+
+DWORD
+CDECL
+PrintError(
+ _In_opt_ HANDLE hModule,
+ _In_ DWORD dwErrId,
+ ...)
+{
+ DPRINT1("PrintError()\n");
+ return 1;
+}
+
+DWORD
+CDECL
+PrintMessageFromModule(
+ _In_ HANDLE hModule,
+ _In_ DWORD dwMsgId,
+ ...)
+{
+ DPRINT1("PrintMessageFromModule()\n");
+ return 1;
+}
+
+DWORD
+CDECL
+PrintMessage(
+ _In_ LPCWSTR pwszFormat,
+ ...)
+{
+ INT Length;
+ va_list ap;
+
+ va_start(ap, pwszFormat);
+ Length = ConPrintf(StdOut, pwszFormat);
+ va_end(ap);
+
+ return Length;
+}
diff --git a/base/applications/network/netsh/netsh.rc
b/base/applications/network/netsh/netsh.rc
new file mode 100644
index 00000000000..448c3239e04
--- /dev/null
+++ b/base/applications/network/netsh/netsh.rc
@@ -0,0 +1,15 @@
+#include <windef.h>
+
+#include "resource.h"
+
+#define REACTOS_STR_FILE_DESCRIPTION "Net Shell"
+#define REACTOS_STR_INTERNAL_NAME "netsh"
+#define REACTOS_STR_ORIGINAL_FILENAME "netsh.exe"
+#include <reactos/version.rc>
+
+/* UTF-8 */
+#pragma code_page(65001)
+
+#ifdef LANGUAGE_EN_US
+ #include "lang/en-US.rc"
+#endif
diff --git a/base/applications/network/netsh/netsh.spec
b/base/applications/network/netsh/netsh.spec
new file mode 100644
index 00000000000..4d82c1e7f36
--- /dev/null
+++ b/base/applications/network/netsh/netsh.spec
@@ -0,0 +1,7 @@
+@ stdcall MatchEnumTag(ptr wstr long ptr ptr)
+@ stdcall MatchToken(wstr wstr)
+@ varargs PrintError(ptr long)
+@ varargs PrintMessage(wstr)
+@ varargs PrintMessageFromModule(ptr long)
+@ stdcall RegisterContext(ptr)
+@ stdcall RegisterHelper(ptr ptr)
diff --git a/base/applications/network/netsh/precomp.h
b/base/applications/network/netsh/precomp.h
new file mode 100644
index 00000000000..031b03cc767
--- /dev/null
+++ b/base/applications/network/netsh/precomp.h
@@ -0,0 +1,205 @@
+/*
+ * PROJECT: ReactOS NetSh
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Network Shell main header file
+ * COPYRIGHT: Copyright 2023 Eric Kohl <eric.kohl(a)reactos.org>
+ */
+
+#ifndef PRECOMP_H
+#define PRECOMP_H
+
+/* INCLUDES ******************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#define WIN32_NO_STATUS
+#include <windef.h>
+#include <winbase.h>
+#include <winreg.h>
+#include <wincon.h>
+#include <winuser.h>
+
+#include <errno.h>
+
+#include <conutils.h>
+#include <netsh.h>
+
+#include "resource.h"
+
+
+/* DEFINES *******************************************************************/
+
+#define MAX_STRING_SIZE 1024
+#define MAX_ARGS_COUNT 256
+
+#define REG_NETSH_PATH L"Software\\Microsoft\\NetSh"
+
+
+/* TYPEDEFS ******************************************************************/
+
+typedef struct _DLL_LIST_ENTRY
+{
+ struct _DLL_LIST_ENTRY *pPrev;
+ struct _DLL_LIST_ENTRY *pNext;
+
+ PWSTR pszDllName;
+ PWSTR pszShortName;
+ PWSTR pszValueName;
+
+ HMODULE hModule;
+
+} DLL_LIST_ENTRY, *PDLL_LIST_ENTRY;
+
+typedef struct _HELPER_ENTRY
+{
+ struct _HELPER_ENTRY *pPrev;
+ struct _HELPER_ENTRY *pNext;
+
+ NS_HELPER_ATTRIBUTES Attributes;
+
+ PDLL_LIST_ENTRY pDllEntry;
+ BOOL bStarted;
+
+ struct _HELPER_ENTRY *pSubHelperHead;
+ struct _HELPER_ENTRY *pSubHelperTail;
+
+} HELPER_ENTRY, *PHELPER_ENTRY;
+
+
+
+typedef struct _COMMAND_ENTRY
+{
+ struct _COMMAND_ENTRY *pPrev;
+ struct _COMMAND_ENTRY *pNext;
+
+ LPCWSTR pwszCmdToken;
+ PFN_HANDLE_CMD pfnCmdHandler;
+ DWORD dwShortCmdHelpToken;
+ DWORD dwCmdHlpToken;
+ DWORD dwFlags;
+} COMMAND_ENTRY, *PCOMMAND_ENTRY;
+
+typedef struct _COMMAND_GROUP
+{
+ struct _COMMAND_GROUP *pPrev;
+ struct _COMMAND_GROUP *pNext;
+
+ LPCWSTR pwszCmdGroupToken;
+ DWORD dwShortCmdHelpToken;
+ DWORD dwFlags;
+
+ PCOMMAND_ENTRY pCommandListHead;
+ PCOMMAND_ENTRY pCommandListTail;
+} COMMAND_GROUP, *PCOMMAND_GROUP;
+
+typedef struct _CONTEXT_ENTRY
+{
+ struct _CONTEXT_ENTRY *pPrev;
+ struct _CONTEXT_ENTRY *pNext;
+
+ struct _CONTEXT_ENTRY *pParentContext;
+
+ PWSTR pszContextName;
+ GUID Guid;
+ HMODULE hModule;
+
+ PCOMMAND_ENTRY pCommandListHead;
+ PCOMMAND_ENTRY pCommandListTail;
+
+ PCOMMAND_GROUP pGroupListHead;
+ PCOMMAND_GROUP pGroupListTail;
+
+ struct _CONTEXT_ENTRY *pSubContextHead;
+ struct _CONTEXT_ENTRY *pSubContextTail;
+} CONTEXT_ENTRY, *PCONTEXT_ENTRY;
+
+
+/* GLOBAL VARIABLES ***********************************************************/
+
+extern PCONTEXT_ENTRY pRootContext;
+extern PCONTEXT_ENTRY pCurrentContext;
+
+
+/* PROTOTYPES *****************************************************************/
+
+/* context.c */
+
+BOOL
+CreateRootContext(VOID);
+
+
+/* help.c */
+DWORD
+WINAPI
+HelpCommand(
+ LPCWSTR pwszMachine,
+ LPWSTR *ppwcArguments,
+ DWORD dwCurrentIndex,
+ DWORD dwArgCount,
+ DWORD dwFlags,
+ LPCVOID pvData,
+ BOOL *pbDone);
+
+VOID
+HelpGroup(
+ PCOMMAND_GROUP pGroup);
+
+
+/* helper.c */
+VOID
+LoadHelpers(VOID);
+
+VOID
+UnloadHelpers(VOID);
+
+
+DWORD
+WINAPI
+AddHelperCommand(
+ LPCWSTR pwszMachine,
+ LPWSTR *ppwcArguments,
+ DWORD dwCurrentIndex,
+ DWORD dwArgCount,
+ DWORD dwFlags,
+ LPCVOID pvData,
+ BOOL *pbDone);
+
+DWORD
+WINAPI
+DeleteHelperCommand(
+ LPCWSTR pwszMachine,
+ LPWSTR *ppwcArguments,
+ DWORD dwCurrentIndex,
+ DWORD dwArgCount,
+ DWORD dwFlags,
+ LPCVOID pvData,
+ BOOL *pbDone);
+
+DWORD
+WINAPI
+ShowHelperCommand(
+ LPCWSTR pwszMachine,
+ PWSTR *ppwcArguments,
+ DWORD dwCurrentIndex,
+ DWORD dwArgCount,
+ DWORD dwFlags,
+ LPCVOID pvData,
+ BOOL *pbDone);
+
+
+/* interpreter.c */
+BOOL
+InterpretScript(
+ LPWSTR pszFileName);
+
+BOOL
+InterpretCommand(
+ LPWSTR *argv,
+ DWORD dwArgCount);
+
+VOID
+InterpretInteractive(VOID);
+
+#endif /* PRECOMP_H */
diff --git a/base/applications/network/netsh/resource.h
b/base/applications/network/netsh/resource.h
new file mode 100644
index 00000000000..e3875fd655c
--- /dev/null
+++ b/base/applications/network/netsh/resource.h
@@ -0,0 +1,37 @@
+/*
+ * PROJECT: ReactOS NetSh
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Network Shell resource id header file
+ * COPYRIGHT: Copyright 2023 Eric Kohl <eric.kohl(a)reactos.org>
+ */
+
+#pragma once
+
+#define IDS_NONE -1
+
+#define IDS_APP_USAGE 100
+#define IDS_APP_PROMPT 101
+#define IDS_INVALID_COMMAND 102
+#define IDS_OPEN_FAILED 103
+#define IDS_INVALID_SYNTAX 104
+
+#define IDS_HELP_HEADER 200
+#define IDS_SUBCONTEXT_HEADER 201
+
+#define IDS_HLP_EXIT 300
+#define IDS_HLP_EXIT_EX 301
+#define IDS_HLP_HELP 302
+#define IDS_HLP_HELP_EX 303
+#define IDS_HLP_UP 304
+#define IDS_HLP_UP_EX 305
+
+#define IDS_HLP_ADD_HELPER 310
+#define IDS_HLP_ADD_HELPER_EX 311
+#define IDS_HLP_DEL_HELPER 312
+#define IDS_HLP_DEL_HELPER_EX 313
+#define IDS_HLP_SHOW_HELPER 314
+#define IDS_HLP_SHOW_HELPER_EX 315
+
+#define IDS_HLP_GROUP_ADD 320
+#define IDS_HLP_GROUP_DELETE 321
+#define IDS_HLP_GROUP_SHOW 322
\ No newline at end of file