[BASESRV-CONSRV-WINSRV]
[reactos.git] / 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 = RtlAllocateHeap(ConSrvHeap, 0, sizeof(HISTORY_BUFFER) + ExeName.Length);
49 if (!Hist)
50 return NULL;
51 Hist->MaxEntries = Console->HistoryBufferSize;
52 Hist->NumEntries = 0;
53 Hist->Entries = RtlAllocateHeap(ConSrvHeap, 0, Hist->MaxEntries * sizeof(UNICODE_STRING));
54 if (!Hist->Entries)
55 {
56 RtlFreeHeap(ConSrvHeap, 0, 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;
71 INT i;
72
73 NewEntry.Length = NewEntry.MaximumLength = Console->LineSize * sizeof(WCHAR);
74 NewEntry.Buffer = Console->LineBuffer;
75
76 if (!(Hist = HistoryCurrentBuffer(Console)))
77 return;
78
79 /* Don't add blank or duplicate entries */
80 if (NewEntry.Length == 0 || Hist->MaxEntries == 0 ||
81 (Hist->NumEntries > 0 &&
82 RtlEqualUnicodeString(&Hist->Entries[Hist->NumEntries - 1], &NewEntry, FALSE)))
83 {
84 return;
85 }
86
87 if (Console->HistoryNoDup)
88 {
89 /* Check if this line has been entered before */
90 for (i = Hist->NumEntries - 1; i >= 0; i--)
91 {
92 if (RtlEqualUnicodeString(&Hist->Entries[i], &NewEntry, FALSE))
93 {
94 /* Just rotate the list to bring this entry to the end */
95 NewEntry = Hist->Entries[i];
96 memmove(&Hist->Entries[i], &Hist->Entries[i + 1],
97 (Hist->NumEntries - (i + 1)) * sizeof(UNICODE_STRING));
98 Hist->Entries[Hist->NumEntries - 1] = NewEntry;
99 Hist->Position = Hist->NumEntries - 1;
100 return;
101 }
102 }
103 }
104
105 if (Hist->NumEntries == Hist->MaxEntries)
106 {
107 /* List is full, remove oldest entry */
108 RtlFreeUnicodeString(&Hist->Entries[0]);
109 memmove(&Hist->Entries[0], &Hist->Entries[1],
110 --Hist->NumEntries * sizeof(UNICODE_STRING));
111 }
112
113 if (NT_SUCCESS(RtlDuplicateUnicodeString(0, &NewEntry, &Hist->Entries[Hist->NumEntries])))
114 Hist->NumEntries++;
115 Hist->Position = Hist->NumEntries - 1;
116 }
117
118 static VOID
119 HistoryGetCurrentEntry(PCONSOLE Console, PUNICODE_STRING Entry)
120 {
121 PHISTORY_BUFFER Hist;
122 if (!(Hist = HistoryCurrentBuffer(Console)) || 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 RtlFreeHeap(ConSrvHeap, 0, Hist->Entries);
152 RemoveEntryList(&Hist->ListEntry);
153 RtlFreeHeap(ConSrvHeap, 0, 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 UINT OldCursorX = Buffer->CursorPosition.X;
177 UINT 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 INT 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;
235
236 if (!(Hist = HistoryCurrentBuffer(Console)) || Hist->NumEntries == 0)
237 return;
238
239 Offset += Hist->Position;
240 Offset = max(Offset, 0);
241 Offset = min(Offset, Hist->NumEntries - 1);
242 Hist->Position = Offset;
243
244 LineInputSetPos(Console, 0);
245 LineInputEdit(Console, Console->LineSize,
246 Hist->Entries[Offset].Length / sizeof(WCHAR),
247 Hist->Entries[Offset].Buffer);
248 }
249
250 VOID FASTCALL
251 LineInputKeyDown(PCONSOLE Console, KEY_EVENT_RECORD *KeyEvent)
252 {
253 UINT Pos = Console->LinePos;
254 PHISTORY_BUFFER Hist;
255 UNICODE_STRING Entry;
256 INT HistPos;
257
258 switch (KeyEvent->wVirtualKeyCode)
259 {
260 case VK_ESCAPE:
261 /* Clear entire line */
262 LineInputSetPos(Console, 0);
263 LineInputEdit(Console, Console->LineSize, 0, NULL);
264 return;
265 case VK_HOME:
266 /* Move to start of line. With ctrl, erase everything left of cursor */
267 LineInputSetPos(Console, 0);
268 if (KeyEvent->dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))
269 LineInputEdit(Console, Pos, 0, NULL);
270 return;
271 case VK_END:
272 /* Move to end of line. With ctrl, erase everything right of cursor */
273 if (KeyEvent->dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))
274 LineInputEdit(Console, Console->LineSize - Pos, 0, NULL);
275 else
276 LineInputSetPos(Console, Console->LineSize);
277 return;
278 case VK_LEFT:
279 /* Move left. With ctrl, move to beginning of previous word */
280 if (KeyEvent->dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))
281 {
282 while (Pos > 0 && Console->LineBuffer[Pos - 1] == L' ') Pos--;
283 while (Pos > 0 && Console->LineBuffer[Pos - 1] != L' ') Pos--;
284 }
285 else
286 {
287 Pos -= (Pos > 0);
288 }
289 LineInputSetPos(Console, Pos);
290 return;
291 case VK_RIGHT:
292 case VK_F1:
293 /* Move right. With ctrl, move to beginning of next word */
294 if (KeyEvent->dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))
295 {
296 while (Pos < Console->LineSize && Console->LineBuffer[Pos] != L' ') Pos++;
297 while (Pos < Console->LineSize && Console->LineBuffer[Pos] == L' ') Pos++;
298 LineInputSetPos(Console, Pos);
299 return;
300 }
301 else
302 {
303 /* Recall one character (but don't overwrite current line) */
304 HistoryGetCurrentEntry(Console, &Entry);
305 if (Pos < Console->LineSize)
306 LineInputSetPos(Console, Pos + 1);
307 else if (Pos * sizeof(WCHAR) < Entry.Length)
308 LineInputEdit(Console, 0, 1, &Entry.Buffer[Pos]);
309 }
310 return;
311 case VK_INSERT:
312 /* Toggle between insert and overstrike */
313 Console->LineInsertToggle = !Console->LineInsertToggle;
314 ConioSetCursorInfo(Console, Console->ActiveBuffer);
315 return;
316 case VK_DELETE:
317 /* Remove character to right of cursor */
318 if (Pos != Console->LineSize)
319 LineInputEdit(Console, 1, 0, NULL);
320 return;
321 case VK_PRIOR:
322 /* Recall first history entry */
323 LineInputRecallHistory(Console, -((WORD)-1));
324 return;
325 case VK_NEXT:
326 /* Recall last history entry */
327 LineInputRecallHistory(Console, +((WORD)-1));
328 return;
329 case VK_UP:
330 case VK_F5:
331 /* Recall previous history entry. On first time, actually recall the
332 * current (usually last) entry; on subsequent times go back. */
333 LineInputRecallHistory(Console, Console->LineUpPressed ? -1 : 0);
334 Console->LineUpPressed = TRUE;
335 return;
336 case VK_DOWN:
337 /* Recall next history entry */
338 LineInputRecallHistory(Console, +1);
339 return;
340 case VK_F3:
341 /* Recall remainder of current history entry */
342 HistoryGetCurrentEntry(Console, &Entry);
343 if (Pos * sizeof(WCHAR) < Entry.Length)
344 {
345 UINT InsertSize = (Entry.Length / sizeof(WCHAR) - Pos);
346 UINT DeleteSize = min(Console->LineSize - Pos, InsertSize);
347 LineInputEdit(Console, DeleteSize, InsertSize, &Entry.Buffer[Pos]);
348 }
349 return;
350 case VK_F6:
351 /* Insert a ^Z character */
352 KeyEvent->uChar.UnicodeChar = 26;
353 break;
354 case VK_F7:
355 if (KeyEvent->dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED))
356 HistoryDeleteBuffer(HistoryCurrentBuffer(Console));
357 return;
358 case VK_F8:
359 /* Search for history entries starting with input. */
360 if (!(Hist = HistoryCurrentBuffer(Console)) || Hist->NumEntries == 0)
361 return;
362
363 /* Like Up/F5, on first time start from current (usually last) entry,
364 * but on subsequent times start at previous entry. */
365 if (Console->LineUpPressed)
366 Hist->Position = (Hist->Position ? Hist->Position : Hist->NumEntries) - 1;
367 Console->LineUpPressed = TRUE;
368
369 Entry.Length = Console->LinePos * sizeof(WCHAR);
370 Entry.Buffer = Console->LineBuffer;
371
372 /* Keep going backwards, even wrapping around to the end,
373 * until we get back to starting point */
374 HistPos = Hist->Position;
375 do
376 {
377 if (RtlPrefixUnicodeString(&Entry, &Hist->Entries[HistPos], FALSE))
378 {
379 Hist->Position = HistPos;
380 LineInputEdit(Console, Console->LineSize - Pos,
381 Hist->Entries[HistPos].Length / sizeof(WCHAR) - Pos,
382 &Hist->Entries[HistPos].Buffer[Pos]);
383 /* Cursor stays where it was */
384 LineInputSetPos(Console, Pos);
385 return;
386 }
387 if (--HistPos < 0) HistPos += Hist->NumEntries;
388 } while (HistPos != Hist->Position);
389 return;
390 }
391
392 if (KeyEvent->uChar.UnicodeChar == L'\b' && Console->InputBuffer.Mode & ENABLE_PROCESSED_INPUT)
393 {
394 /* backspace handling - if processed input enabled then we handle it here
395 * otherwise we treat it like a normal char. */
396 if (Pos > 0)
397 {
398 LineInputSetPos(Console, Pos - 1);
399 LineInputEdit(Console, 1, 0, NULL);
400 }
401 }
402 else if (KeyEvent->uChar.UnicodeChar == L'\r')
403 {
404 HistoryAddEntry(Console);
405
406 /* TODO: Expand aliases */
407
408 LineInputSetPos(Console, Console->LineSize);
409 Console->LineBuffer[Console->LineSize++] = L'\r';
410 if (Console->InputBuffer.Mode & ENABLE_ECHO_INPUT)
411 ConioWriteConsole(Console, Console->ActiveBuffer, "\r", 1, TRUE);
412
413 /* Add \n if processed input. There should usually be room for it,
414 * but an exception to the rule exists: the buffer could have been
415 * pre-filled with LineMaxSize - 1 characters. */
416 if (Console->InputBuffer.Mode & ENABLE_PROCESSED_INPUT &&
417 Console->LineSize < Console->LineMaxSize)
418 {
419 Console->LineBuffer[Console->LineSize++] = L'\n';
420 if (Console->InputBuffer.Mode & ENABLE_ECHO_INPUT)
421 ConioWriteConsole(Console, Console->ActiveBuffer, "\n", 1, TRUE);
422 }
423 Console->LineComplete = TRUE;
424 Console->LinePos = 0;
425 }
426 else if (KeyEvent->uChar.UnicodeChar != L'\0')
427 {
428 if (KeyEvent->uChar.UnicodeChar < 0x20 &&
429 Console->LineWakeupMask & (1 << KeyEvent->uChar.UnicodeChar))
430 {
431 /* Control key client wants to handle itself (e.g. for tab completion) */
432 Console->LineBuffer[Console->LineSize++] = L' ';
433 Console->LineBuffer[Console->LinePos] = KeyEvent->uChar.UnicodeChar;
434 Console->LineComplete = TRUE;
435 Console->LinePos = 0;
436 }
437 else
438 {
439 /* Normal character */
440 BOOL Overstrike = Console->LineInsertToggle && Console->LinePos != Console->LineSize;
441 LineInputEdit(Console, Overstrike, 1, &KeyEvent->uChar.UnicodeChar);
442 }
443 }
444 }
445
446
447 /* PUBLIC SERVER APIS *********************************************************/
448
449 CSR_API(SrvGetConsoleCommandHistory)
450 {
451 PCONSOLE_GETCOMMANDHISTORY GetCommandHistoryRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetCommandHistoryRequest;
452 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
453 PCONSOLE Console;
454 NTSTATUS Status;
455 PHISTORY_BUFFER Hist;
456 PBYTE Buffer = (PBYTE)GetCommandHistoryRequest->History;
457 ULONG BufferSize = GetCommandHistoryRequest->Length;
458 INT i;
459
460 if ( !CsrValidateMessageBuffer(ApiMessage,
461 (PVOID*)&GetCommandHistoryRequest->History,
462 GetCommandHistoryRequest->Length,
463 sizeof(BYTE)) ||
464 !CsrValidateMessageBuffer(ApiMessage,
465 (PVOID*)&GetCommandHistoryRequest->ExeName.Buffer,
466 GetCommandHistoryRequest->ExeName.Length,
467 sizeof(BYTE)) )
468 {
469 return STATUS_INVALID_PARAMETER;
470 }
471
472 Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
473 if (NT_SUCCESS(Status))
474 {
475 Hist = HistoryFindBuffer(Console, &GetCommandHistoryRequest->ExeName);
476 if (Hist)
477 {
478 for (i = 0; i < Hist->NumEntries; i++)
479 {
480 if (BufferSize < (Hist->Entries[i].Length + sizeof(WCHAR)))
481 {
482 Status = STATUS_BUFFER_OVERFLOW;
483 break;
484 }
485 memcpy(Buffer, Hist->Entries[i].Buffer, Hist->Entries[i].Length);
486 Buffer += Hist->Entries[i].Length;
487 *(PWCHAR)Buffer = L'\0';
488 Buffer += sizeof(WCHAR);
489 }
490 }
491 GetCommandHistoryRequest->Length = Buffer - (PBYTE)GetCommandHistoryRequest->History;
492 ConSrvReleaseConsole(Console, TRUE);
493 }
494 return Status;
495 }
496
497 CSR_API(SrvGetConsoleCommandHistoryLength)
498 {
499 PCONSOLE_GETCOMMANDHISTORYLENGTH GetCommandHistoryLengthRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetCommandHistoryLengthRequest;
500 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
501 PCONSOLE Console;
502 NTSTATUS Status;
503 PHISTORY_BUFFER Hist;
504 ULONG Length = 0;
505 INT i;
506
507 if (!CsrValidateMessageBuffer(ApiMessage,
508 (PVOID*)&GetCommandHistoryLengthRequest->ExeName.Buffer,
509 GetCommandHistoryLengthRequest->ExeName.Length,
510 sizeof(BYTE)))
511 {
512 return STATUS_INVALID_PARAMETER;
513 }
514
515 Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
516 if (NT_SUCCESS(Status))
517 {
518 Hist = HistoryFindBuffer(Console, &GetCommandHistoryLengthRequest->ExeName);
519 if (Hist)
520 {
521 for (i = 0; i < Hist->NumEntries; i++)
522 Length += Hist->Entries[i].Length + sizeof(WCHAR);
523 }
524 GetCommandHistoryLengthRequest->Length = Length;
525 ConSrvReleaseConsole(Console, TRUE);
526 }
527 return Status;
528 }
529
530 CSR_API(SrvExpungeConsoleCommandHistory)
531 {
532 PCONSOLE_EXPUNGECOMMANDHISTORY ExpungeCommandHistoryRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ExpungeCommandHistoryRequest;
533 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
534 PCONSOLE Console;
535 PHISTORY_BUFFER Hist;
536 NTSTATUS Status;
537
538 if (!CsrValidateMessageBuffer(ApiMessage,
539 (PVOID*)&ExpungeCommandHistoryRequest->ExeName.Buffer,
540 ExpungeCommandHistoryRequest->ExeName.Length,
541 sizeof(BYTE)))
542 {
543 return STATUS_INVALID_PARAMETER;
544 }
545
546 Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
547 if (NT_SUCCESS(Status))
548 {
549 Hist = HistoryFindBuffer(Console, &ExpungeCommandHistoryRequest->ExeName);
550 HistoryDeleteBuffer(Hist);
551 ConSrvReleaseConsole(Console, TRUE);
552 }
553 return Status;
554 }
555
556 CSR_API(SrvSetConsoleNumberOfCommands)
557 {
558 PCONSOLE_SETHISTORYNUMBERCOMMANDS SetHistoryNumberCommandsRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.SetHistoryNumberCommandsRequest;
559 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
560 PCONSOLE Console;
561 PHISTORY_BUFFER Hist;
562 NTSTATUS Status;
563 UINT MaxEntries = SetHistoryNumberCommandsRequest->NumCommands;
564 PUNICODE_STRING OldEntryList, NewEntryList;
565
566 if (!CsrValidateMessageBuffer(ApiMessage,
567 (PVOID*)&SetHistoryNumberCommandsRequest->ExeName.Buffer,
568 SetHistoryNumberCommandsRequest->ExeName.Length,
569 sizeof(BYTE)))
570 {
571 return STATUS_INVALID_PARAMETER;
572 }
573
574 Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
575 if (NT_SUCCESS(Status))
576 {
577 Hist = HistoryFindBuffer(Console, &SetHistoryNumberCommandsRequest->ExeName);
578 if (Hist)
579 {
580 OldEntryList = Hist->Entries;
581 NewEntryList = RtlAllocateHeap(ConSrvHeap, 0,
582 MaxEntries * sizeof(UNICODE_STRING));
583 if (!NewEntryList)
584 {
585 Status = STATUS_NO_MEMORY;
586 }
587 else
588 {
589 /* If necessary, shrink by removing oldest entries */
590 for (; Hist->NumEntries > MaxEntries; Hist->NumEntries--)
591 {
592 RtlFreeUnicodeString(Hist->Entries++);
593 Hist->Position += (Hist->Position == 0);
594 }
595
596 Hist->MaxEntries = MaxEntries;
597 Hist->Entries = memcpy(NewEntryList, Hist->Entries,
598 Hist->NumEntries * sizeof(UNICODE_STRING));
599 RtlFreeHeap(ConSrvHeap, 0, OldEntryList);
600 }
601 }
602 ConSrvReleaseConsole(Console, TRUE);
603 }
604 return Status;
605 }
606
607 CSR_API(SrvGetConsoleHistory)
608 {
609 PCONSOLE_GETSETHISTORYINFO HistoryInfoRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.HistoryInfoRequest;
610 PCONSOLE Console;
611 NTSTATUS Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
612 if (NT_SUCCESS(Status))
613 {
614 HistoryInfoRequest->HistoryBufferSize = Console->HistoryBufferSize;
615 HistoryInfoRequest->NumberOfHistoryBuffers = Console->NumberOfHistoryBuffers;
616 HistoryInfoRequest->dwFlags = Console->HistoryNoDup;
617 ConSrvReleaseConsole(Console, TRUE);
618 }
619 return Status;
620 }
621
622 CSR_API(SrvSetConsoleHistory)
623 {
624 PCONSOLE_GETSETHISTORYINFO HistoryInfoRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.HistoryInfoRequest;
625 PCONSOLE Console;
626 NTSTATUS Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
627 if (NT_SUCCESS(Status))
628 {
629 Console->HistoryBufferSize = HistoryInfoRequest->HistoryBufferSize;
630 Console->NumberOfHistoryBuffers = HistoryInfoRequest->NumberOfHistoryBuffers;
631 Console->HistoryNoDup = HistoryInfoRequest->dwFlags & HISTORY_NO_DUP_FLAG;
632 ConSrvReleaseConsole(Console, TRUE);
633 }
634 return Status;
635 }
636
637 /* EOF */