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