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