xref: /reactos/ntoskrnl/kdbg/kdb_cmdhist.c (revision 3c797b31)
1 /*
2  * PROJECT:     ReactOS KDBG Kernel Debugger
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Command History helpers
5  * COPYRIGHT:   Copyright 2005 Gregor Anich <blight@blight.eu.org>
6  *              Copyright 2023 Hermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
7  */
8 
9 /* INCLUDES ******************************************************************/
10 
11 #include <ntoskrnl.h>
12 
13 /* GLOBALS *******************************************************************/
14 
15 /**
16  * History range in ring buffer:
17  * (KdbCommandHistoryIndex; RTL_NUMBER_OF(KdbCommandHistory) - 1]
18  *   and [0; KdbCommandHistoryIndex].
19  **/
20 
21 /* Command history string ring buffer */
22 static CHAR KdbCommandHistoryBuffer[2048];
23 /* Command history ring buffer */
24 static PCHAR KdbCommandHistory[sizeof(KdbCommandHistoryBuffer) / 8] = { NULL };
25 static LONG KdbCommandHistoryBufferIndex = 0;
26 static LONG KdbCommandHistoryIndex = 0;
27 
28 /* FUNCTIONS *****************************************************************/
29 
30 /**
31  * @brief   Appends a command to the command history.
32  *
33  * @param[in]   Command
34  * Pointer to the command to append to the history.
35  **/
36 VOID
37 KdbpCommandHistoryAppend(
38     _In_ PCSTR Command)
39 {
40     SIZE_T Length1 = strlen(Command) + 1;
41     SIZE_T Length2 = 0;
42     LONG i;
43     PCHAR Buffer;
44 
45     ASSERT(Length1 <= RTL_NUMBER_OF(KdbCommandHistoryBuffer));
46 
47     /*
48      * Do not append the string if:
49      * - it is empty (just the NULL terminator);
50      * - or the last command is the same.
51      */
52     if (Length1 <= 1 ||
53         (KdbCommandHistory[KdbCommandHistoryIndex] &&
54          strcmp(KdbCommandHistory[KdbCommandHistoryIndex], Command) == 0))
55     {
56         return;
57     }
58 
59     /* Calculate Length1 and Length2 */
60     Buffer = KdbCommandHistoryBuffer + KdbCommandHistoryBufferIndex;
61     KdbCommandHistoryBufferIndex += Length1;
62     if (KdbCommandHistoryBufferIndex >= (LONG)RTL_NUMBER_OF(KdbCommandHistoryBuffer))
63     {
64         KdbCommandHistoryBufferIndex -= RTL_NUMBER_OF(KdbCommandHistoryBuffer);
65         Length2 = KdbCommandHistoryBufferIndex;
66         Length1 -= Length2;
67     }
68 
69     /* Remove previous commands until there is enough space to append the new command */
70     for (i = KdbCommandHistoryIndex; KdbCommandHistory[i];)
71     {
72         if ((Length2 > 0 &&
73             (KdbCommandHistory[i] >= Buffer ||
74              KdbCommandHistory[i] < (KdbCommandHistoryBuffer + KdbCommandHistoryBufferIndex))) ||
75             (Length2 <= 0 &&
76              (KdbCommandHistory[i] >= Buffer &&
77               KdbCommandHistory[i] < (KdbCommandHistoryBuffer + KdbCommandHistoryBufferIndex))))
78         {
79             KdbCommandHistory[i] = NULL;
80         }
81 
82         i--;
83         if (i < 0)
84             i = RTL_NUMBER_OF(KdbCommandHistory) - 1;
85 
86         if (i == KdbCommandHistoryIndex)
87             break;
88     }
89 
90     /* Make sure the new command history entry is free */
91     KdbCommandHistoryIndex++;
92     KdbCommandHistoryIndex %= RTL_NUMBER_OF(KdbCommandHistory);
93     if (KdbCommandHistory[KdbCommandHistoryIndex])
94     {
95         KdbCommandHistory[KdbCommandHistoryIndex] = NULL;
96     }
97 
98     /* Append command */
99     KdbCommandHistory[KdbCommandHistoryIndex] = Buffer;
100     ASSERT((KdbCommandHistory[KdbCommandHistoryIndex] + Length1) <= KdbCommandHistoryBuffer + RTL_NUMBER_OF(KdbCommandHistoryBuffer));
101     memcpy(KdbCommandHistory[KdbCommandHistoryIndex], Command, Length1);
102     if (Length2 > 0)
103     {
104         memcpy(KdbCommandHistoryBuffer, Command + Length1, Length2);
105     }
106 }
107 
108 static PCSTR
109 KdbpGetPrevHistoryEntry(
110     _Inout_ PLONG NextIndex)
111 {
112     if (*NextIndex < 0)
113     {
114         /* Start at the end of the history */
115         *NextIndex = KdbCommandHistoryIndex;
116     }
117     else
118     {
119         LONG i = *NextIndex - 1;
120         if (i < 0)
121             *NextIndex = RTL_NUMBER_OF(KdbCommandHistory) - 1;
122 
123         if (KdbCommandHistory[i] && i != KdbCommandHistoryIndex)
124             *NextIndex = i;
125         else
126             return NULL;
127     }
128 
129     return KdbCommandHistory[*NextIndex];
130 }
131 
132 static PCSTR
133 KdbpGetNextHistoryEntry(
134     _Inout_ PLONG NextIndex)
135 {
136     LONG i;
137 
138     if (!(*NextIndex > 0 && *NextIndex != KdbCommandHistoryIndex))
139         return NULL;
140 
141     i = *NextIndex + 1;
142     if (i >= (LONG)RTL_NUMBER_OF(KdbCommandHistory))
143         i = 0;
144 
145     if (KdbCommandHistory[i])
146         *NextIndex = i;
147 
148     return KdbCommandHistory[i];
149 }
150 
151 PCSTR
152 KdbGetHistoryEntry(
153     _Inout_ PLONG NextIndex,
154     _In_ BOOLEAN Next)
155 {
156     if (Next)
157         return KdbpGetNextHistoryEntry(NextIndex);
158     else
159         return KdbpGetPrevHistoryEntry(NextIndex);
160 }
161 
162 /* EOF */
163