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