224d6781cebb7975b23cd1d27453df52dc2f9d48
[reactos.git] / reactos / win32ss / user / consrv / lineinput.c
1 /*
2 * LICENSE: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Console Server DLL
4 * FILE: win32ss/user/consrv/lineinput.c
5 * PURPOSE: Console line input functions
6 * PROGRAMMERS: Jeffrey Morlan
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include "consrv.h"
12 #include "console.h"
13 #include "include/conio.h"
14 #include "conio.h"
15
16 #define NDEBUG
17 #include <debug.h>
18
19 typedef struct _HISTORY_BUFFER
20 {
21 LIST_ENTRY ListEntry;
22 UINT Position;
23 UINT MaxEntries;
24 UINT NumEntries;
25 PUNICODE_STRING Entries;
26 UNICODE_STRING ExeName;
27 } HISTORY_BUFFER, *PHISTORY_BUFFER;
28
29
30 /* PRIVATE FUNCTIONS **********************************************************/
31
32 static PHISTORY_BUFFER
33 HistoryCurrentBuffer(PCONSOLE Console)
34 {
35 /* TODO: use actual EXE name sent from process that called ReadConsole */
36 UNICODE_STRING ExeName = { 14, 14, L"cmd.exe" };
37 PLIST_ENTRY Entry = Console->HistoryBuffers.Flink;
38 PHISTORY_BUFFER Hist;
39
40 for (; Entry != &Console->HistoryBuffers; Entry = Entry->Flink)
41 {
42 Hist = CONTAINING_RECORD(Entry, HISTORY_BUFFER, ListEntry);
43 if (RtlEqualUnicodeString(&ExeName, &Hist->ExeName, FALSE))
44 return Hist;
45 }
46
47 /* Couldn't find the buffer, create a new one */
48 Hist = ConsoleAllocHeap(0, sizeof(HISTORY_BUFFER) + ExeName.Length);
49 if (!Hist)
50 return NULL;
51 Hist->MaxEntries = Console->HistoryBufferSize;
52 Hist->NumEntries = 0;
53 Hist->Entries = ConsoleAllocHeap(0, Hist->MaxEntries * sizeof(UNICODE_STRING));
54 if (!Hist->Entries)
55 {
56 ConsoleFreeHeap(Hist);
57 return NULL;
58 }
59 Hist->ExeName.Length = Hist->ExeName.MaximumLength = ExeName.Length;
60 Hist->ExeName.Buffer = (PWCHAR)(Hist + 1);
61 memcpy(Hist->ExeName.Buffer, ExeName.Buffer, ExeName.Length);
62 InsertHeadList(&Console->HistoryBuffers, &Hist->ListEntry);
63 return Hist;
64 }
65
66 static VOID
67 HistoryAddEntry(PCONSOLE Console)
68 {
69 UNICODE_STRING NewEntry;
70 PHISTORY_BUFFER Hist = HistoryCurrentBuffer(Console);
71 INT i;
72
73 if (!Hist) return;
74
75 NewEntry.Length = NewEntry.MaximumLength = Console->LineSize * sizeof(WCHAR);
76 NewEntry.Buffer = Console->LineBuffer;
77
78 /* Don't add blank or duplicate entries */
79 if (NewEntry.Length == 0 || Hist->MaxEntries == 0 ||
80 (Hist->NumEntries > 0 &&
81 RtlEqualUnicodeString(&Hist->Entries[Hist->NumEntries - 1], &NewEntry, FALSE)))
82 {
83 return;
84 }
85
86 if (Console->HistoryNoDup)
87 {
88 /* Check if this line has been entered before */
89 for (i = Hist->NumEntries - 1; i >= 0; i--)
90 {
91 if (RtlEqualUnicodeString(&Hist->Entries[i], &NewEntry, FALSE))
92 {
93 /* Just rotate the list to bring this entry to the end */
94 NewEntry = Hist->Entries[i];
95 memmove(&Hist->Entries[i], &Hist->Entries[i + 1],
96 (Hist->NumEntries - (i + 1)) * sizeof(UNICODE_STRING));
97 Hist->Entries[Hist->NumEntries - 1] = NewEntry;
98 Hist->Position = Hist->NumEntries - 1;
99 return;
100 }
101 }
102 }
103
104 if (Hist->NumEntries == Hist->MaxEntries)
105 {
106 /* List is full, remove oldest entry */
107 RtlFreeUnicodeString(&Hist->Entries[0]);
108 memmove(&Hist->Entries[0], &Hist->Entries[1],
109 --Hist->NumEntries * sizeof(UNICODE_STRING));
110 }
111
112 if (NT_SUCCESS(RtlDuplicateUnicodeString(0, &NewEntry, &Hist->Entries[Hist->NumEntries])))
113 Hist->NumEntries++;
114 Hist->Position = Hist->NumEntries - 1;
115 }
116
117 static VOID
118 HistoryGetCurrentEntry(PCONSOLE Console, PUNICODE_STRING Entry)
119 {
120 PHISTORY_BUFFER Hist = HistoryCurrentBuffer(Console);
121
122 if (!Hist || Hist->NumEntries == 0)
123 Entry->Length = 0;
124 else
125 *Entry = Hist->Entries[Hist->Position];
126 }
127
128 static PHISTORY_BUFFER
129 HistoryFindBuffer(PCONSOLE Console, PUNICODE_STRING ExeName)
130 {
131 PLIST_ENTRY Entry = Console->HistoryBuffers.Flink;
132 while (Entry != &Console->HistoryBuffers)
133 {
134 /* For the history APIs, the caller is allowed to give only part of the name */
135 PHISTORY_BUFFER Hist = CONTAINING_RECORD(Entry, HISTORY_BUFFER, ListEntry);
136 if (RtlPrefixUnicodeString(ExeName, &Hist->ExeName, TRUE))
137 return Hist;
138 Entry = Entry->Flink;
139 }
140 return NULL;
141 }
142
143 static VOID
144 HistoryDeleteBuffer(PHISTORY_BUFFER Hist)
145 {
146 if (!Hist) return;
147
148 while (Hist->NumEntries != 0)
149 RtlFreeUnicodeString(&Hist->Entries[--Hist->NumEntries]);
150
151 ConsoleFreeHeap(Hist->Entries);
152 RemoveEntryList(&Hist->ListEntry);
153 ConsoleFreeHeap(Hist);
154 }
155
156 VOID FASTCALL
157 HistoryDeleteBuffers(PCONSOLE Console)
158 {
159 PLIST_ENTRY CurrentEntry;
160 PHISTORY_BUFFER HistoryBuffer;
161
162 while (!IsListEmpty(&Console->HistoryBuffers))
163 {
164 CurrentEntry = RemoveHeadList(&Console->HistoryBuffers);
165 HistoryBuffer = CONTAINING_RECORD(CurrentEntry, HISTORY_BUFFER, ListEntry);
166 HistoryDeleteBuffer(HistoryBuffer);
167 }
168 }
169
170 static VOID
171 LineInputSetPos(PCONSOLE Console, UINT Pos)
172 {
173 if (Pos != Console->LinePos && Console->InputBuffer.Mode & ENABLE_ECHO_INPUT)
174 {
175 PCONSOLE_SCREEN_BUFFER Buffer = Console->ActiveBuffer;
176 SHORT OldCursorX = Buffer->CursorPosition.X;
177 SHORT OldCursorY = Buffer->CursorPosition.Y;
178 INT XY = OldCursorY * Buffer->ScreenBufferSize.X + OldCursorX;
179
180 XY += (Pos - Console->LinePos);
181 if (XY < 0)
182 XY = 0;
183 else if (XY >= Buffer->ScreenBufferSize.Y * Buffer->ScreenBufferSize.X)
184 XY = Buffer->ScreenBufferSize.Y * Buffer->ScreenBufferSize.X - 1;
185
186 Buffer->CursorPosition.X = XY % Buffer->ScreenBufferSize.X;
187 Buffer->CursorPosition.Y = XY / Buffer->ScreenBufferSize.X;
188 ConioSetScreenInfo(Console, Buffer, OldCursorX, OldCursorY);
189 }
190
191 Console->LinePos = Pos;
192 }
193
194 static VOID
195 LineInputEdit(PCONSOLE Console, UINT NumToDelete, UINT NumToInsert, WCHAR *Insertion)
196 {
197 UINT Pos = Console->LinePos;
198 UINT NewSize = Console->LineSize - NumToDelete + NumToInsert;
199 UINT i;
200
201 /* Make sure there's always enough room for ending \r\n */
202 if (NewSize + 2 > Console->LineMaxSize)
203 return;
204
205 memmove(&Console->LineBuffer[Pos + NumToInsert],
206 &Console->LineBuffer[Pos + NumToDelete],
207 (Console->LineSize - (Pos + NumToDelete)) * sizeof(WCHAR));
208 memcpy(&Console->LineBuffer[Pos], Insertion, NumToInsert * sizeof(WCHAR));
209
210 if (Console->InputBuffer.Mode & ENABLE_ECHO_INPUT)
211 {
212 for (i = Pos; i < NewSize; i++)
213 {
214 CHAR AsciiChar;
215 WideCharToMultiByte(Console->OutputCodePage, 0,
216 &Console->LineBuffer[i], 1,
217 &AsciiChar, 1, NULL, NULL);
218 ConioWriteConsole(Console, Console->ActiveBuffer, &AsciiChar, 1, TRUE);
219 }
220 for (; i < Console->LineSize; i++)
221 {
222 ConioWriteConsole(Console, Console->ActiveBuffer, " ", 1, TRUE);
223 }
224 Console->LinePos = i;
225 }
226
227 Console->LineSize = NewSize;
228 LineInputSetPos(Console, Pos + NumToInsert);
229 }
230
231 static VOID
232 LineInputRecallHistory(PCONSOLE Console, INT Offset)
233 {
234 PHISTORY_BUFFER Hist = HistoryCurrentBuffer(Console);
235 UINT Position = 0;
236
237 if (!Hist || Hist->NumEntries == 0) return;
238
239 Position = Hist->Position + Offset;
240 Position = min(max(Position, 0), Hist->NumEntries - 1);
241 Hist->Position = Position;
242
243 LineInputSetPos(Console, 0);
244 LineInputEdit(Console, Console->LineSize,
245 Hist->Entries[Hist->Position].Length / sizeof(WCHAR),
246 Hist->Entries[Hist->Position].Buffer);
247 }
248
249 VOID FASTCALL
250 LineInputKeyDown(PCONSOLE Console, KEY_EVENT_RECORD *KeyEvent)
251 {
252 UINT Pos = Console->LinePos;
253 PHISTORY_BUFFER Hist;
254 UNICODE_STRING Entry;
255 INT HistPos;
256
257 switch (KeyEvent->wVirtualKeyCode)
258 {
259 case VK_ESCAPE:
260 /* Clear entire line */
261 LineInputSetPos(Console, 0);
262 LineInputEdit(Console, Console->LineSize, 0, NULL);
263 return;
264 case VK_HOME:
265 /* Move to start of line. With ctrl, erase everything left of cursor */
266 LineInputSetPos(Console, 0);
267 if (KeyEvent->dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))
268 LineInputEdit(Console, Pos, 0, NULL);
269 return;
270 case VK_END:
271 /* Move to end of line. With ctrl, erase everything right of cursor */
272 if (KeyEvent->dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))
273 LineInputEdit(Console, Console->LineSize - Pos, 0, NULL);
274 else
275 LineInputSetPos(Console, Console->LineSize);
276 return;
277 case VK_LEFT:
278 /* Move left. With ctrl, move to beginning of previous word */
279 if (KeyEvent->dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))
280 {
281 while (Pos > 0 && Console->LineBuffer[Pos - 1] == L' ') Pos--;
282 while (Pos > 0 && Console->LineBuffer[Pos - 1] != L' ') Pos--;
283 }
284 else
285 {
286 Pos -= (Pos > 0);
287 }
288 LineInputSetPos(Console, Pos);
289 return;
290 case VK_RIGHT:
291 case VK_F1:
292 /* Move right. With ctrl, move to beginning of next word */
293 if (KeyEvent->dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))
294 {
295 while (Pos < Console->LineSize && Console->LineBuffer[Pos] != L' ') Pos++;
296 while (Pos < Console->LineSize && Console->LineBuffer[Pos] == L' ') Pos++;
297 LineInputSetPos(Console, Pos);
298 return;
299 }
300 else
301 {
302 /* Recall one character (but don't overwrite current line) */
303 HistoryGetCurrentEntry(Console, &Entry);
304 if (Pos < Console->LineSize)
305 LineInputSetPos(Console, Pos + 1);
306 else if (Pos * sizeof(WCHAR) < Entry.Length)
307 LineInputEdit(Console, 0, 1, &Entry.Buffer[Pos]);
308 }
309 return;
310 case VK_INSERT:
311 /* Toggle between insert and overstrike */
312 Console->LineInsertToggle = !Console->LineInsertToggle;
313 ConioSetCursorInfo(Console, Console->ActiveBuffer);
314 return;
315 case VK_DELETE:
316 /* Remove character to right of cursor */
317 if (Pos != Console->LineSize)
318 LineInputEdit(Console, 1, 0, NULL);
319 return;
320 case VK_PRIOR:
321 /* Recall first history entry */
322 LineInputRecallHistory(Console, -((WORD)-1));
323 return;
324 case VK_NEXT:
325 /* Recall last history entry */
326 LineInputRecallHistory(Console, +((WORD)-1));
327 return;
328 case VK_UP:
329 case VK_F5:
330 /* Recall previous history entry. On first time, actually recall the
331 * current (usually last) entry; on subsequent times go back. */
332 LineInputRecallHistory(Console, Console->LineUpPressed ? -1 : 0);
333 Console->LineUpPressed = TRUE;
334 return;
335 case VK_DOWN:
336 /* Recall next history entry */
337 LineInputRecallHistory(Console, +1);
338 return;
339 case VK_F3:
340 /* Recall remainder of current history entry */
341 HistoryGetCurrentEntry(Console, &Entry);
342 if (Pos * sizeof(WCHAR) < Entry.Length)
343 {
344 UINT InsertSize = (Entry.Length / sizeof(WCHAR) - Pos);
345 UINT DeleteSize = min(Console->LineSize - Pos, InsertSize);
346 LineInputEdit(Console, DeleteSize, InsertSize, &Entry.Buffer[Pos]);
347 }
348 return;
349 case VK_F6:
350 /* Insert a ^Z character */
351 KeyEvent->uChar.UnicodeChar = 26;
352 break;
353 case VK_F7:
354 if (KeyEvent->dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED))
355 HistoryDeleteBuffer(HistoryCurrentBuffer(Console));
356 return;
357 case VK_F8:
358 /* Search for history entries starting with input. */
359 Hist = HistoryCurrentBuffer(Console);
360 if (!Hist || Hist->NumEntries == 0) return;
361
362 /* Like Up/F5, on first time start from current (usually last) entry,
363 * but on subsequent times start at previous entry. */
364 if (Console->LineUpPressed)
365 Hist->Position = (Hist->Position ? Hist->Position : Hist->NumEntries) - 1;
366 Console->LineUpPressed = TRUE;
367
368 Entry.Length = Console->LinePos * sizeof(WCHAR);
369 Entry.Buffer = Console->LineBuffer;
370
371 /* Keep going backwards, even wrapping around to the end,
372 * until we get back to starting point */
373 HistPos = Hist->Position;
374 do
375 {
376 if (RtlPrefixUnicodeString(&Entry, &Hist->Entries[HistPos], FALSE))
377 {
378 Hist->Position = HistPos;
379 LineInputEdit(Console, Console->LineSize - Pos,
380 Hist->Entries[HistPos].Length / sizeof(WCHAR) - Pos,
381 &Hist->Entries[HistPos].Buffer[Pos]);
382 /* Cursor stays where it was */
383 LineInputSetPos(Console, Pos);
384 return;
385 }
386 if (--HistPos < 0) HistPos += Hist->NumEntries;
387 } while (HistPos != Hist->Position);
388 return;
389 }
390
391 if (KeyEvent->uChar.UnicodeChar == L'\b' && Console->InputBuffer.Mode & ENABLE_PROCESSED_INPUT)
392 {
393 /* backspace handling - if processed input enabled then we handle it here
394 * otherwise we treat it like a normal char. */
395 if (Pos > 0)
396 {
397 LineInputSetPos(Console, Pos - 1);
398 LineInputEdit(Console, 1, 0, NULL);
399 }
400 }
401 else if (KeyEvent->uChar.UnicodeChar == L'\r')
402 {
403 HistoryAddEntry(Console);
404
405 /* TODO: Expand aliases */
406
407 LineInputSetPos(Console, Console->LineSize);
408 Console->LineBuffer[Console->LineSize++] = L'\r';
409 if (Console->InputBuffer.Mode & ENABLE_ECHO_INPUT)
410 ConioWriteConsole(Console, Console->ActiveBuffer, "\r", 1, TRUE);
411
412 /* Add \n if processed input. There should usually be room for it,
413 * but an exception to the rule exists: the buffer could have been
414 * pre-filled with LineMaxSize - 1 characters. */
415 if (Console->InputBuffer.Mode & ENABLE_PROCESSED_INPUT &&
416 Console->LineSize < Console->LineMaxSize)
417 {
418 Console->LineBuffer[Console->LineSize++] = L'\n';
419 if (Console->InputBuffer.Mode & ENABLE_ECHO_INPUT)
420 ConioWriteConsole(Console, Console->ActiveBuffer, "\n", 1, TRUE);
421 }
422 Console->LineComplete = TRUE;
423 Console->LinePos = 0;
424 }
425 else if (KeyEvent->uChar.UnicodeChar != L'\0')
426 {
427 if (KeyEvent->uChar.UnicodeChar < 0x20 &&
428 Console->LineWakeupMask & (1 << KeyEvent->uChar.UnicodeChar))
429 {
430 /* Control key client wants to handle itself (e.g. for tab completion) */
431 Console->LineBuffer[Console->LineSize++] = L' ';
432 Console->LineBuffer[Console->LinePos] = KeyEvent->uChar.UnicodeChar;
433 Console->LineComplete = TRUE;
434 Console->LinePos = 0;
435 }
436 else
437 {
438 /* Normal character */
439 BOOL Overstrike = Console->LineInsertToggle && Console->LinePos != Console->LineSize;
440 LineInputEdit(Console, Overstrike, 1, &KeyEvent->uChar.UnicodeChar);
441 }
442 }
443 }
444
445
446 /* PUBLIC SERVER APIS *********************************************************/
447
448 CSR_API(SrvGetConsoleCommandHistory)
449 {
450 PCONSOLE_GETCOMMANDHISTORY GetCommandHistoryRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetCommandHistoryRequest;
451 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
452 PCONSOLE Console;
453 NTSTATUS Status;
454 PHISTORY_BUFFER Hist;
455 PBYTE Buffer = (PBYTE)GetCommandHistoryRequest->History;
456 ULONG BufferSize = GetCommandHistoryRequest->Length;
457 UINT i;
458
459 if ( !CsrValidateMessageBuffer(ApiMessage,
460 (PVOID*)&GetCommandHistoryRequest->History,
461 GetCommandHistoryRequest->Length,
462 sizeof(BYTE)) ||
463 !CsrValidateMessageBuffer(ApiMessage,
464 (PVOID*)&GetCommandHistoryRequest->ExeName.Buffer,
465 GetCommandHistoryRequest->ExeName.Length,
466 sizeof(BYTE)) )
467 {
468 return STATUS_INVALID_PARAMETER;
469 }
470
471 Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
472 if (NT_SUCCESS(Status))
473 {
474 Hist = HistoryFindBuffer(Console, &GetCommandHistoryRequest->ExeName);
475 if (Hist)
476 {
477 for (i = 0; i < Hist->NumEntries; i++)
478 {
479 if (BufferSize < (Hist->Entries[i].Length + sizeof(WCHAR)))
480 {
481 Status = STATUS_BUFFER_OVERFLOW;
482 break;
483 }
484 memcpy(Buffer, Hist->Entries[i].Buffer, Hist->Entries[i].Length);
485 Buffer += Hist->Entries[i].Length;
486 *(PWCHAR)Buffer = L'\0';
487 Buffer += sizeof(WCHAR);
488 }
489 }
490 GetCommandHistoryRequest->Length = Buffer - (PBYTE)GetCommandHistoryRequest->History;
491 ConSrvReleaseConsole(Console, TRUE);
492 }
493 return Status;
494 }
495
496 CSR_API(SrvGetConsoleCommandHistoryLength)
497 {
498 PCONSOLE_GETCOMMANDHISTORYLENGTH GetCommandHistoryLengthRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetCommandHistoryLengthRequest;
499 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
500 PCONSOLE Console;
501 NTSTATUS Status;
502 PHISTORY_BUFFER Hist;
503 ULONG Length = 0;
504 UINT i;
505
506 if (!CsrValidateMessageBuffer(ApiMessage,
507 (PVOID*)&GetCommandHistoryLengthRequest->ExeName.Buffer,
508 GetCommandHistoryLengthRequest->ExeName.Length,
509 sizeof(BYTE)))
510 {
511 return STATUS_INVALID_PARAMETER;
512 }
513
514 Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
515 if (NT_SUCCESS(Status))
516 {
517 Hist = HistoryFindBuffer(Console, &GetCommandHistoryLengthRequest->ExeName);
518 if (Hist)
519 {
520 for (i = 0; i < Hist->NumEntries; i++)
521 Length += Hist->Entries[i].Length + sizeof(WCHAR);
522 }
523 GetCommandHistoryLengthRequest->Length = Length;
524 ConSrvReleaseConsole(Console, TRUE);
525 }
526 return Status;
527 }
528
529 CSR_API(SrvExpungeConsoleCommandHistory)
530 {
531 PCONSOLE_EXPUNGECOMMANDHISTORY ExpungeCommandHistoryRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ExpungeCommandHistoryRequest;
532 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
533 PCONSOLE Console;
534 PHISTORY_BUFFER Hist;
535 NTSTATUS Status;
536
537 if (!CsrValidateMessageBuffer(ApiMessage,
538 (PVOID*)&ExpungeCommandHistoryRequest->ExeName.Buffer,
539 ExpungeCommandHistoryRequest->ExeName.Length,
540 sizeof(BYTE)))
541 {
542 return STATUS_INVALID_PARAMETER;
543 }
544
545 Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
546 if (NT_SUCCESS(Status))
547 {
548 Hist = HistoryFindBuffer(Console, &ExpungeCommandHistoryRequest->ExeName);
549 HistoryDeleteBuffer(Hist);
550 ConSrvReleaseConsole(Console, TRUE);
551 }
552 return Status;
553 }
554
555 CSR_API(SrvSetConsoleNumberOfCommands)
556 {
557 PCONSOLE_SETHISTORYNUMBERCOMMANDS SetHistoryNumberCommandsRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.SetHistoryNumberCommandsRequest;
558 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
559 PCONSOLE Console;
560 PHISTORY_BUFFER Hist;
561 NTSTATUS Status;
562 UINT MaxEntries = SetHistoryNumberCommandsRequest->NumCommands;
563 PUNICODE_STRING OldEntryList, NewEntryList;
564
565 if (!CsrValidateMessageBuffer(ApiMessage,
566 (PVOID*)&SetHistoryNumberCommandsRequest->ExeName.Buffer,
567 SetHistoryNumberCommandsRequest->ExeName.Length,
568 sizeof(BYTE)))
569 {
570 return STATUS_INVALID_PARAMETER;
571 }
572
573 Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
574 if (NT_SUCCESS(Status))
575 {
576 Hist = HistoryFindBuffer(Console, &SetHistoryNumberCommandsRequest->ExeName);
577 if (Hist)
578 {
579 OldEntryList = Hist->Entries;
580 NewEntryList = ConsoleAllocHeap(0, MaxEntries * sizeof(UNICODE_STRING));
581 if (!NewEntryList)
582 {
583 Status = STATUS_NO_MEMORY;
584 }
585 else
586 {
587 /* If necessary, shrink by removing oldest entries */
588 for (; Hist->NumEntries > MaxEntries; Hist->NumEntries--)
589 {
590 RtlFreeUnicodeString(Hist->Entries++);
591 Hist->Position += (Hist->Position == 0);
592 }
593
594 Hist->MaxEntries = MaxEntries;
595 Hist->Entries = memcpy(NewEntryList, Hist->Entries,
596 Hist->NumEntries * sizeof(UNICODE_STRING));
597 ConsoleFreeHeap(OldEntryList);
598 }
599 }
600 ConSrvReleaseConsole(Console, TRUE);
601 }
602 return Status;
603 }
604
605 CSR_API(SrvGetConsoleHistory)
606 {
607 PCONSOLE_GETSETHISTORYINFO HistoryInfoRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.HistoryInfoRequest;
608 PCONSOLE Console;
609 NTSTATUS Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
610 if (NT_SUCCESS(Status))
611 {
612 HistoryInfoRequest->HistoryBufferSize = Console->HistoryBufferSize;
613 HistoryInfoRequest->NumberOfHistoryBuffers = Console->NumberOfHistoryBuffers;
614 HistoryInfoRequest->dwFlags = Console->HistoryNoDup;
615 ConSrvReleaseConsole(Console, TRUE);
616 }
617 return Status;
618 }
619
620 CSR_API(SrvSetConsoleHistory)
621 {
622 PCONSOLE_GETSETHISTORYINFO HistoryInfoRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.HistoryInfoRequest;
623 PCONSOLE Console;
624 NTSTATUS Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
625 if (NT_SUCCESS(Status))
626 {
627 Console->HistoryBufferSize = HistoryInfoRequest->HistoryBufferSize;
628 Console->NumberOfHistoryBuffers = HistoryInfoRequest->NumberOfHistoryBuffers;
629 Console->HistoryNoDup = HistoryInfoRequest->dwFlags & HISTORY_NO_DUP_FLAG;
630 ConSrvReleaseConsole(Console, TRUE);
631 }
632 return Status;
633 }
634
635 /* EOF */