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