[KERNEL32][CONSRV]
[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
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
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 NTSTATUS Status;
459 PCONSOLE_GETCOMMANDHISTORY GetCommandHistoryRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetCommandHistoryRequest;
460 PCONSOLE Console;
461 PHISTORY_BUFFER Hist;
462 UNICODE_STRING ExeName;
463 PBYTE Buffer = (PBYTE)GetCommandHistoryRequest->History;
464 ULONG BufferSize = GetCommandHistoryRequest->HistoryLength;
465 UINT i;
466
467 if ( !CsrValidateMessageBuffer(ApiMessage,
468 (PVOID*)&GetCommandHistoryRequest->History,
469 GetCommandHistoryRequest->HistoryLength,
470 sizeof(BYTE)) ||
471 !CsrValidateMessageBuffer(ApiMessage,
472 (PVOID*)&GetCommandHistoryRequest->ExeName,
473 GetCommandHistoryRequest->ExeLength,
474 sizeof(BYTE)) )
475 {
476 return STATUS_INVALID_PARAMETER;
477 }
478
479 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
480 if (!NT_SUCCESS(Status)) return Status;
481
482 // FIXME: convert to UNICODE if Unicode(2) == FALSE
483 ExeName.Length = ExeName.MaximumLength = GetCommandHistoryRequest->ExeLength;
484 ExeName.Buffer = GetCommandHistoryRequest->ExeName;
485
486 Hist = HistoryFindBuffer(Console, &ExeName);
487 if (Hist)
488 {
489 for (i = 0; i < Hist->NumEntries; i++)
490 {
491 if (BufferSize < (Hist->Entries[i].Length + sizeof(WCHAR)))
492 {
493 Status = STATUS_BUFFER_OVERFLOW;
494 break;
495 }
496 // FIXME: convert to UNICODE if Unicode == FALSE
497 memcpy(Buffer, Hist->Entries[i].Buffer, Hist->Entries[i].Length);
498 Buffer += Hist->Entries[i].Length;
499 *(PWCHAR)Buffer = L'\0';
500 Buffer += sizeof(WCHAR);
501
502 // {
503 // WideCharToMultiByte(CP_ACP, 0,
504 // GetCommandHistoryRequest->History,
505 // GetCommandHistoryRequest->HistoryLength / sizeof(WCHAR),
506 // lpHistory,
507 // cbHistory,
508 // NULL, NULL);
509 // }
510
511 }
512 }
513 // FIXME: convert to UNICODE if Unicode == FALSE
514 GetCommandHistoryRequest->HistoryLength = Buffer - (PBYTE)GetCommandHistoryRequest->History;
515
516 ConSrvReleaseConsole(Console, TRUE);
517 return Status;
518 }
519
520 CSR_API(SrvGetConsoleCommandHistoryLength)
521 {
522 NTSTATUS Status;
523 PCONSOLE_GETCOMMANDHISTORYLENGTH GetCommandHistoryLengthRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetCommandHistoryLengthRequest;
524 PCONSOLE Console;
525 PHISTORY_BUFFER Hist;
526 UNICODE_STRING ExeName;
527 ULONG Length = 0;
528 UINT i;
529
530 if (!CsrValidateMessageBuffer(ApiMessage,
531 (PVOID*)&GetCommandHistoryLengthRequest->ExeName,
532 GetCommandHistoryLengthRequest->ExeLength,
533 sizeof(BYTE)))
534 {
535 return STATUS_INVALID_PARAMETER;
536 }
537
538 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
539 if (!NT_SUCCESS(Status)) return Status;
540
541 // FIXME: convert to UNICODE if Unicode(2) == FALSE
542 ExeName.Length = ExeName.MaximumLength = GetCommandHistoryLengthRequest->ExeLength;
543 ExeName.Buffer = GetCommandHistoryLengthRequest->ExeName;
544
545 Hist = HistoryFindBuffer(Console, &ExeName);
546 if (Hist)
547 {
548 for (i = 0; i < Hist->NumEntries; i++)
549 Length += Hist->Entries[i].Length + sizeof(WCHAR);
550 }
551 GetCommandHistoryLengthRequest->HistoryLength = Length;
552
553 /*
554 * Quick and dirty way of getting the number of bytes of the
555 * corresponding ANSI string from the one in UNICODE.
556 */
557 if (!GetCommandHistoryLengthRequest->Unicode)
558 GetCommandHistoryLengthRequest->HistoryLength /= sizeof(WCHAR);
559
560 ConSrvReleaseConsole(Console, TRUE);
561 return Status;
562 }
563
564 CSR_API(SrvExpungeConsoleCommandHistory)
565 {
566 NTSTATUS Status;
567 PCONSOLE_EXPUNGECOMMANDHISTORY ExpungeCommandHistoryRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ExpungeCommandHistoryRequest;
568 PCONSOLE Console;
569 PHISTORY_BUFFER Hist;
570 UNICODE_STRING ExeName;
571
572 if (!CsrValidateMessageBuffer(ApiMessage,
573 (PVOID*)&ExpungeCommandHistoryRequest->ExeName,
574 ExpungeCommandHistoryRequest->ExeLength,
575 sizeof(BYTE)))
576 {
577 return STATUS_INVALID_PARAMETER;
578 }
579
580 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
581 if (!NT_SUCCESS(Status)) return Status;
582
583 // FIXME: convert to UNICODE if Unicode(2) == FALSE
584 ExeName.Length = ExeName.MaximumLength = ExpungeCommandHistoryRequest->ExeLength;
585 ExeName.Buffer = ExpungeCommandHistoryRequest->ExeName;
586
587 Hist = HistoryFindBuffer(Console, &ExeName);
588 HistoryDeleteBuffer(Hist);
589
590 ConSrvReleaseConsole(Console, TRUE);
591 return Status;
592 }
593
594 CSR_API(SrvSetConsoleNumberOfCommands)
595 {
596 NTSTATUS Status;
597 PCONSOLE_SETHISTORYNUMBERCOMMANDS SetHistoryNumberCommandsRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.SetHistoryNumberCommandsRequest;
598 PCONSOLE Console;
599 PHISTORY_BUFFER Hist;
600 UINT MaxEntries = SetHistoryNumberCommandsRequest->NumCommands;
601 UNICODE_STRING ExeName;
602 PUNICODE_STRING OldEntryList, NewEntryList;
603
604 if (!CsrValidateMessageBuffer(ApiMessage,
605 (PVOID*)&SetHistoryNumberCommandsRequest->ExeName,
606 SetHistoryNumberCommandsRequest->ExeLength,
607 sizeof(BYTE)))
608 {
609 return STATUS_INVALID_PARAMETER;
610 }
611
612 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
613 if (!NT_SUCCESS(Status)) return Status;
614
615 // FIXME: convert to UNICODE if Unicode(2) == FALSE
616 ExeName.Length = ExeName.MaximumLength = SetHistoryNumberCommandsRequest->ExeLength;
617 ExeName.Buffer = SetHistoryNumberCommandsRequest->ExeName;
618
619 Hist = HistoryFindBuffer(Console, &ExeName);
620 if (Hist)
621 {
622 OldEntryList = Hist->Entries;
623 NewEntryList = ConsoleAllocHeap(0, MaxEntries * sizeof(UNICODE_STRING));
624 if (!NewEntryList)
625 {
626 Status = STATUS_NO_MEMORY;
627 }
628 else
629 {
630 /* If necessary, shrink by removing oldest entries */
631 for (; Hist->NumEntries > MaxEntries; Hist->NumEntries--)
632 {
633 RtlFreeUnicodeString(Hist->Entries++);
634 Hist->Position += (Hist->Position == 0);
635 }
636
637 Hist->MaxEntries = MaxEntries;
638 Hist->Entries = memcpy(NewEntryList, Hist->Entries,
639 Hist->NumEntries * sizeof(UNICODE_STRING));
640 ConsoleFreeHeap(OldEntryList);
641 }
642 }
643
644 ConSrvReleaseConsole(Console, TRUE);
645 return Status;
646 }
647
648 CSR_API(SrvGetConsoleHistory)
649 {
650 PCONSOLE_GETSETHISTORYINFO HistoryInfoRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.HistoryInfoRequest;
651 PCONSOLE Console;
652 NTSTATUS Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
653 if (NT_SUCCESS(Status))
654 {
655 HistoryInfoRequest->HistoryBufferSize = Console->HistoryBufferSize;
656 HistoryInfoRequest->NumberOfHistoryBuffers = Console->NumberOfHistoryBuffers;
657 HistoryInfoRequest->dwFlags = Console->HistoryNoDup;
658 ConSrvReleaseConsole(Console, TRUE);
659 }
660 return Status;
661 }
662
663 CSR_API(SrvSetConsoleHistory)
664 {
665 PCONSOLE_GETSETHISTORYINFO HistoryInfoRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.HistoryInfoRequest;
666 PCONSOLE Console;
667 NTSTATUS Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
668 if (NT_SUCCESS(Status))
669 {
670 Console->HistoryBufferSize = HistoryInfoRequest->HistoryBufferSize;
671 Console->NumberOfHistoryBuffers = HistoryInfoRequest->NumberOfHistoryBuffers;
672 Console->HistoryNoDup = HistoryInfoRequest->dwFlags & HISTORY_NO_DUP_FLAG;
673 ConSrvReleaseConsole(Console, TRUE);
674 }
675 return Status;
676 }
677
678 CSR_API(SrvSetConsoleCommandHistoryMode)
679 {
680 NTSTATUS Status;
681 PCONSOLE_SETHISTORYMODE SetHistoryModeRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.SetHistoryModeRequest;
682 PCONSOLE Console;
683
684 DPRINT1("SrvSetConsoleCommandHistoryMode(Mode = %d) is not yet implemented\n",
685 SetHistoryModeRequest->Mode);
686
687 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
688 if (!NT_SUCCESS(Status)) return Status;
689
690 /* This API is not yet implemented */
691 Status = STATUS_NOT_IMPLEMENTED;
692
693 ConSrvReleaseConsole(Console, TRUE);
694 return Status;
695 }
696
697 /* EOF */