https://git.reactos.org/?p=reactos.git;a=commitdiff;h=f3dd7133823a298596605…
commit f3dd7133823a2985966059e52630e08ef4849cf5
Author: Hermès Bélusca-Maïto <hermes.belusca-maito(a)reactos.org>
AuthorDate: Mon Mar 20 17:49:15 2023 +0100
Commit: Hermès Bélusca-Maïto <hermes.belusca-maito(a)reactos.org>
CommitDate: Tue Mar 28 16:14:40 2023 +0200
[NTOS:KD:KDBG] Isolate the read-line (prompt) functionality in a separate file.
Rename KdbpReadCommand as KdIoReadLine. Extract the last-command
repetition functionality out of KdIoReadLine and put it where it
belongs: only in the KDBG command main loop KdbpCliMainLoop.
---
ntoskrnl/kd/kdio.c | 5 +-
ntoskrnl/kd/kdmain.c | 1 +
ntoskrnl/kd/kdprompt.c | 166 +++++++++++++++++++++++++++++++++++++
ntoskrnl/kd/kdserial.c | 2 +
ntoskrnl/kd/kdterminal.h | 29 +++++++
ntoskrnl/kdbg/kdb.h | 5 --
ntoskrnl/kdbg/kdb_cli.c | 209 ++++++-----------------------------------------
ntoskrnl/ntos.cmake | 1 +
8 files changed, 226 insertions(+), 192 deletions(-)
diff --git a/ntoskrnl/kd/kdio.c b/ntoskrnl/kd/kdio.c
index f9e7b38abe1..a196b905113 100644
--- a/ntoskrnl/kd/kdio.c
+++ b/ntoskrnl/kd/kdio.c
@@ -12,6 +12,7 @@
#include <ntoskrnl.h>
#include <reactos/buildno.h>
#include "kd.h"
+#include "kdterminal.h"
#define NDEBUG
#include <debug.h>
@@ -779,8 +780,8 @@ KdReceivePacket(
* in which case the string is simply truncated without NULL-termination.
*/
ResponseString.Length =
- (USHORT)KdbpReadCommand(ResponseString.Buffer,
- ResponseString.MaximumLength);
+ (USHORT)KdIoReadLine(ResponseString.Buffer,
+ ResponseString.MaximumLength);
if (!(KdbDebugState & KD_DEBUG_KDSERIAL))
KbdEnableMouse();
diff --git a/ntoskrnl/kd/kdmain.c b/ntoskrnl/kd/kdmain.c
index de341bad204..aaed45432db 100644
--- a/ntoskrnl/kd/kdmain.c
+++ b/ntoskrnl/kd/kdmain.c
@@ -9,6 +9,7 @@
#include <ntoskrnl.h>
#include "kd.h"
+
#define NDEBUG
#include <debug.h>
diff --git a/ntoskrnl/kd/kdprompt.c b/ntoskrnl/kd/kdprompt.c
new file mode 100644
index 00000000000..f9ac04f6d03
--- /dev/null
+++ b/ntoskrnl/kd/kdprompt.c
@@ -0,0 +1,166 @@
+/*
+ * PROJECT: ReactOS KDBG Kernel Debugger Terminal Driver
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Terminal line-editing (Prompt) interface
+ * COPYRIGHT: Copyright 2001-2004 David Welch <welch(a)cwcom.net>
+ * Copyright 2004-2005 Gregor Anich <blight(a)blight.eu.org>
+ * Copyright 2022-2023 Hermès Bélusca-Maïto
<hermes.belusca-maito(a)reactos.org>
+ */
+
+/* INCLUDES ******************************************************************/
+
+#include <ntoskrnl.h>
+#include "kdterminal.h"
+
+/* FUNCTIONS *****************************************************************/
+
+/**
+ * @brief Reads a line of user input from the terminal.
+ *
+ * @param[out] Buffer
+ * Buffer where to store the input. Trailing newlines are removed.
+ *
+ * @param[in] Size
+ * Size of @p Buffer.
+ *
+ * @return
+ * Returns the number of characters stored, not counting the NULL terminator.
+ *
+ * @note Accepts only \n newlines, \r is ignored.
+ **/
+SIZE_T
+KdIoReadLine(
+ _Out_ PCHAR Buffer,
+ _In_ SIZE_T Size)
+{
+ PCHAR Orig = Buffer;
+ ULONG ScanCode = 0;
+ CHAR Key;
+ static CHAR NextKey = ANSI_NULL;
+ BOOLEAN EchoOn;
+ LONG CmdHistIndex = -1; // Start at end of history.
+
+ /* Bail out if the buffer is zero-sized */
+ if (Size == 0)
+ return 0;
+
+ EchoOn = ((KdbDebugState & KD_DEBUG_KDNOECHO) == 0);
+
+ for (;;)
+ {
+ ScanCode = 0;
+ if (KdbDebugState & KD_DEBUG_KDSERIAL)
+ {
+ Key = (!NextKey ? KdbpGetCharSerial() : NextKey);
+ NextKey = ANSI_NULL;
+ if (Key == KEY_ESC) /* ESC */
+ {
+ Key = KdbpGetCharSerial();
+ if (Key == '[')
+ {
+ Key = KdbpGetCharSerial();
+
+ switch (Key)
+ {
+ case 'A':
+ ScanCode = KEY_SCAN_UP;
+ break;
+ case 'B':
+ ScanCode = KEY_SCAN_DOWN;
+ break;
+ case 'C':
+ break;
+ case 'D':
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ Key = (!NextKey ? KdbpGetCharKeyboard(&ScanCode) : NextKey);
+ NextKey = ANSI_NULL;
+ }
+
+ /* Check for return or newline */
+ if ((Key == '\r') || (Key == '\n'))
+ {
+ if (Key == '\r')
+ {
+ /*
+ * We might need to discard the next '\n' which most clients
+ * should send after \r. Wait a bit to make sure we receive it.
+ */
+ KeStallExecutionProcessor(100000);
+
+ if (KdbDebugState & KD_DEBUG_KDSERIAL)
+ NextKey = KdbpTryGetCharSerial(5);
+ else
+ NextKey = KdbpTryGetCharKeyboard(&ScanCode, 5);
+
+ if (NextKey == '\n' || NextKey == -1) /* \n or no response at all
*/
+ NextKey = ANSI_NULL;
+ }
+
+ *Buffer = ANSI_NULL;
+ KdIoPuts("\n");
+ return (SIZE_T)(Buffer - Orig);
+ }
+ else if (Key == KEY_BS || Key == KEY_DEL)
+ {
+ /* Erase the last character */
+ if (Buffer > Orig)
+ {
+ Buffer--;
+ *Buffer = ANSI_NULL;
+
+ if (EchoOn)
+ KdIoPrintf("%c %c", KEY_BS, KEY_BS);
+ else
+ KdIoPrintf(" %c", KEY_BS);
+ }
+ }
+ else if (ScanCode == KEY_SCAN_UP || ScanCode == KEY_SCAN_DOWN)
+ {
+ PCSTR CmdHistory = KdbGetHistoryEntry(&CmdHistIndex,
+ (ScanCode == KEY_SCAN_DOWN));
+ if (CmdHistory)
+ {
+ SIZE_T i;
+
+ /* Erase the whole line */
+ while (Buffer > Orig)
+ {
+ Buffer--;
+ *Buffer = ANSI_NULL;
+
+ if (EchoOn)
+ KdIoPrintf("%c %c", KEY_BS, KEY_BS);
+ else
+ KdIoPrintf(" %c", KEY_BS);
+ }
+
+ /* Copy and display the history entry */
+ i = min(strlen(CmdHistory), Size - 1);
+ memcpy(Orig, CmdHistory, i);
+ Orig[i] = ANSI_NULL;
+ Buffer = Orig + i;
+ KdIoPuts(Orig);
+ }
+ }
+ else
+ {
+ /* Do not accept characters anymore if the buffer is full */
+ if ((SIZE_T)(Buffer - Orig) >= (Size - 1))
+ continue;
+
+ if (EchoOn)
+ KdIoPrintf("%c", Key);
+
+ *Buffer = Key;
+ Buffer++;
+ }
+ }
+}
+
+/* EOF */
diff --git a/ntoskrnl/kd/kdserial.c b/ntoskrnl/kd/kdserial.c
index d36b7abe559..d854d92aca2 100644
--- a/ntoskrnl/kd/kdserial.c
+++ b/ntoskrnl/kd/kdserial.c
@@ -34,3 +34,5 @@ KdbpTryGetCharSerial(
return Result;
}
+
+/* EOF */
diff --git a/ntoskrnl/kd/kdterminal.h b/ntoskrnl/kd/kdterminal.h
new file mode 100644
index 00000000000..d5aab3fe48c
--- /dev/null
+++ b/ntoskrnl/kd/kdterminal.h
@@ -0,0 +1,29 @@
+/*
+ * PROJECT: ReactOS KDBG Kernel Debugger Terminal Driver
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: KD Terminal Driver public header
+ * COPYRIGHT: Copyright 2023 Hermès Bélusca-Maïto
<hermes.belusca-maito(a)reactos.org>
+ */
+
+#pragma once
+
+#define KEY_BS 8
+#define KEY_ESC 27
+#define KEY_DEL 127
+
+#define KEY_SCAN_UP 72
+#define KEY_SCAN_DOWN 80
+
+/* Scan codes of keyboard keys */
+#define KEYSC_END 0x004f
+#define KEYSC_PAGEUP 0x0049
+#define KEYSC_PAGEDOWN 0x0051
+#define KEYSC_HOME 0x0047
+#define KEYSC_ARROWUP 0x0048 // == KEY_SCAN_UP
+
+SIZE_T
+KdIoReadLine(
+ _Out_ PCHAR Buffer,
+ _In_ SIZE_T Size);
+
+/* EOF */
diff --git a/ntoskrnl/kdbg/kdb.h b/ntoskrnl/kdbg/kdb.h
index bf8a47e5ec1..26c79577030 100644
--- a/ntoskrnl/kdbg/kdb.h
+++ b/ntoskrnl/kdbg/kdb.h
@@ -113,11 +113,6 @@ KdbGetHistoryEntry(
_Inout_ PLONG NextIndex,
_In_ BOOLEAN Next);
-SIZE_T
-KdbpReadCommand(
- _Out_ PCHAR Buffer,
- _In_ SIZE_T Size);
-
VOID
KdbpPager(
_In_ PCHAR Buffer,
diff --git a/ntoskrnl/kdbg/kdb_cli.c b/ntoskrnl/kdbg/kdb_cli.c
index ad28412fd91..f909fecdfc7 100644
--- a/ntoskrnl/kdbg/kdb_cli.c
+++ b/ntoskrnl/kdbg/kdb_cli.c
@@ -29,26 +29,13 @@
/* INCLUDES ******************************************************************/
#include <ntoskrnl.h>
+#include "../kd/kdterminal.h"
#define NDEBUG
#include <debug.h>
/* DEFINES *******************************************************************/
-#define KEY_BS 8
-#define KEY_ESC 27
-#define KEY_DEL 127
-
-#define KEY_SCAN_UP 72
-#define KEY_SCAN_DOWN 80
-
-/* Scan codes of keyboard keys: */
-#define KEYSC_END 0x004f
-#define KEYSC_PAGEUP 0x0049
-#define KEYSC_PAGEDOWN 0x0051
-#define KEYSC_HOME 0x0047
-#define KEYSC_ARROWUP 0x0048
-
#define KDB_ENTER_CONDITION_TO_STRING(cond) \
((cond) == KdbDoNotEnter ? "never" : \
((cond) == KdbEnterAlways ? "always" : \
@@ -3253,172 +3240,6 @@ KdbpPrintUnicodeString(
}
-/**
- * @brief Reads a line of user input from the terminal.
- *
- * @param[out] Buffer
- * Buffer where to store the input. Trailing newlines are removed.
- *
- * @param[in] Size
- * Size of \a Buffer.
- *
- * @return
- * Returns the number of characters stored, not counting the NULL terminator.
- *
- * @note Accepts only \n newlines, \r is ignored.
- **/
-SIZE_T
-KdbpReadCommand(
- _Out_ PCHAR Buffer,
- _In_ SIZE_T Size)
-{
- PCHAR Orig = Buffer;
- ULONG ScanCode = 0;
- CHAR Key;
- BOOLEAN EchoOn;
- static CHAR LastCommand[1024];
- static CHAR NextKey = '\0';
- LONG CmdHistIndex = -1; // Start at end of history.
-
- /* Bail out if the buffer is zero-sized */
- if (Size == 0)
- return 0;
-
- EchoOn = ((KdbDebugState & KD_DEBUG_KDNOECHO) == 0);
-
- for (;;)
- {
- if (KdbDebugState & KD_DEBUG_KDSERIAL)
- {
- Key = (NextKey == '\0') ? KdbpGetCharSerial() : NextKey;
- NextKey = '\0';
- ScanCode = 0;
- if (Key == KEY_ESC) /* ESC */
- {
- Key = KdbpGetCharSerial();
- if (Key == '[')
- {
- Key = KdbpGetCharSerial();
-
- switch (Key)
- {
- case 'A':
- ScanCode = KEY_SCAN_UP;
- break;
- case 'B':
- ScanCode = KEY_SCAN_DOWN;
- break;
- case 'C':
- break;
- case 'D':
- break;
- }
- }
- }
- }
- else
- {
- ScanCode = 0;
- Key = (NextKey == '\0') ? KdbpGetCharKeyboard(&ScanCode) :
NextKey;
- NextKey = '\0';
- }
-
- /* Check for return or newline */
- if ((Key == '\r') || (Key == '\n'))
- {
- if (Key == '\r')
- {
- /*
- * We might need to discard the next '\n' which most clients
- * should send after \r. Wait a bit to make sure we receive it.
- */
- KeStallExecutionProcessor(100000);
-
- if (KdbDebugState & KD_DEBUG_KDSERIAL)
- NextKey = KdbpTryGetCharSerial(5);
- else
- NextKey = KdbpTryGetCharKeyboard(&ScanCode, 5);
-
- if (NextKey == '\n' || NextKey == -1) /* \n or no response at all
*/
- NextKey = '\0';
- }
-
- KdpDprintf("\n");
-
- /*
- * Repeat the last command if the user presses enter. Reduces the
- * risk of RSI when single-stepping.
- */
- if (Buffer != Orig)
- {
- KdbRepeatLastCommand = TRUE;
- *Buffer = '\0';
- RtlStringCbCopyA(LastCommand, sizeof(LastCommand), Orig);
- }
- else if (KdbRepeatLastCommand)
- RtlStringCbCopyA(Buffer, Size, LastCommand);
- else
- *Buffer = '\0';
-
- return (SIZE_T)(Buffer - Orig);
- }
- else if (Key == KEY_BS || Key == KEY_DEL)
- {
- /* Erase the last character */
- if (Buffer > Orig)
- {
- Buffer--;
- *Buffer = '\0';
-
- if (EchoOn)
- KdpDprintf("%c %c", KEY_BS, KEY_BS);
- else
- KdpDprintf(" %c", KEY_BS);
- }
- }
- else if (ScanCode == KEY_SCAN_UP || ScanCode == KEY_SCAN_DOWN)
- {
- PCSTR CmdHistory = KdbGetHistoryEntry(&CmdHistIndex,
- (ScanCode == KEY_SCAN_DOWN));
- if (CmdHistory)
- {
- SIZE_T i;
-
- /* Erase the whole line */
- while (Buffer > Orig)
- {
- Buffer--;
- *Buffer = '\0';
-
- if (EchoOn)
- KdpDprintf("%c %c", KEY_BS, KEY_BS);
- else
- KdpDprintf(" %c", KEY_BS);
- }
-
- i = min(strlen(CmdHistory), Size - 1);
- memcpy(Orig, CmdHistory, i);
- Orig[i] = '\0';
- Buffer = Orig + i;
- KdpDprintf("%s", Orig);
- }
- }
- else
- {
- /* Don't accept any key if the buffer is full */
- if ((SIZE_T)(Buffer - Orig) >= (Size - 1))
- continue;
-
- if (EchoOn)
- KdpDprintf("%c", Key);
-
- *Buffer = Key;
- Buffer++;
- }
- }
-}
-
-
BOOLEAN
NTAPI
KdbRegisterCliCallback(
@@ -3567,8 +3388,10 @@ VOID
KdbpCliMainLoop(
IN BOOLEAN EnteredOnSingleStep)
{
- static CHAR Command[1024];
BOOLEAN Continue;
+ SIZE_T CmdLen;
+ static CHAR Command[1024];
+ static CHAR LastCommand[1024] = "";
if (EnteredOnSingleStep)
{
@@ -3603,11 +3426,27 @@ KdbpCliMainLoop(
KdbNumberOfRowsPrinted = KdbNumberOfColsPrinted = 0;
/* Print the prompt */
- KdbpPrint(KdbPromptString.Buffer);
+ KdpDprintf(KdbPromptString.Buffer);
+
+ /*
+ * Read a command. Repeat the last one if the user pressed Enter.
+ * This reduces the risk of RSI when single-stepping!
+ */
+ CmdLen = KdIoReadLine(Command, sizeof(Command));
+ if (CmdLen > 0) // i.e. (*Command != ANSI_NULL)
+ {
+ /* Save this new last command */
+ KdbRepeatLastCommand = TRUE;
+ RtlStringCbCopyA(LastCommand, sizeof(LastCommand), Command);
- /* Read a command and remember it */
- KdbpReadCommand(Command, sizeof(Command));
- KdbpCommandHistoryAppend(Command);
+ /* Remember it */
+ KdbpCommandHistoryAppend(Command);
+ }
+ else if (KdbRepeatLastCommand)
+ {
+ /* The user directly pressed Enter */
+ RtlStringCbCopyA(Command, sizeof(Command), LastCommand);
+ }
/* Reset the number of rows/cols printed and output aborted state */
KdbNumberOfRowsPrinted = KdbNumberOfColsPrinted = 0;
diff --git a/ntoskrnl/ntos.cmake b/ntoskrnl/ntos.cmake
index 648de8faf6b..8df4eacccc3 100644
--- a/ntoskrnl/ntos.cmake
+++ b/ntoskrnl/ntos.cmake
@@ -413,6 +413,7 @@ if(NOT _WINKD_)
list(APPEND SOURCE
${REACTOS_SOURCE_DIR}/ntoskrnl/kd/kdio.c
${REACTOS_SOURCE_DIR}/ntoskrnl/kd/kdmain.c
+ ${REACTOS_SOURCE_DIR}/ntoskrnl/kd/kdprompt.c
${REACTOS_SOURCE_DIR}/ntoskrnl/kd/kdps2kbd.c
${REACTOS_SOURCE_DIR}/ntoskrnl/kd/kdserial.c)