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