[CONSRV]: Code cleaning.
[reactos.git] / win32ss / user / winsrv / consrv / history.c
1 /*
2 * LICENSE: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Console Server DLL
4 * FILE: win32ss/user/winsrv/consrv/history.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 ULONG Position;
20 ULONG MaxEntries;
21 ULONG 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 PHISTORY_BUFFER
78 HistoryFindBuffer(PCONSRV_CONSOLE Console,
79 PVOID ExeName,
80 USHORT ExeLength,
81 BOOLEAN UnicodeExe)
82 {
83 UNICODE_STRING ExeNameU;
84
85 PLIST_ENTRY Entry;
86 PHISTORY_BUFFER Hist = NULL;
87
88 if (ExeName == NULL) return NULL;
89
90 if (UnicodeExe)
91 {
92 ExeNameU.Buffer = ExeName;
93 /* Length is in bytes */
94 ExeNameU.MaximumLength = ExeLength;
95 }
96 else
97 {
98 if (!ConvertInputAnsiToUnicode(Console,
99 ExeName, ExeLength,
100 &ExeNameU.Buffer, &ExeNameU.MaximumLength))
101 {
102 return NULL;
103 }
104 }
105 ExeNameU.Length = ExeNameU.MaximumLength;
106
107 Entry = Console->HistoryBuffers.Flink;
108 while (Entry != &Console->HistoryBuffers)
109 {
110 Hist = CONTAINING_RECORD(Entry, HISTORY_BUFFER, ListEntry);
111
112 /* For the history APIs, the caller is allowed to give only part of the name */
113 if (RtlPrefixUnicodeString(&ExeNameU, &Hist->ExeName, TRUE))
114 {
115 if (!UnicodeExe) ConsoleFreeHeap(ExeNameU.Buffer);
116 return Hist;
117 }
118
119 Entry = Entry->Flink;
120 }
121
122 if (!UnicodeExe) ConsoleFreeHeap(ExeNameU.Buffer);
123 return NULL;
124 }
125
126 static VOID
127 HistoryDeleteBuffer(PHISTORY_BUFFER Hist)
128 {
129 if (!Hist) return;
130
131 while (Hist->NumEntries != 0)
132 RtlFreeUnicodeString(&Hist->Entries[--Hist->NumEntries]);
133
134 ConsoleFreeHeap(Hist->Entries);
135 RemoveEntryList(&Hist->ListEntry);
136 ConsoleFreeHeap(Hist);
137 }
138
139 VOID
140 HistoryAddEntry(PCONSRV_CONSOLE Console,
141 PUNICODE_STRING ExeName,
142 PUNICODE_STRING Entry)
143 {
144 // UNICODE_STRING NewEntry;
145 PHISTORY_BUFFER Hist = HistoryCurrentBuffer(Console, ExeName);
146
147 if (!Hist) return;
148
149 // NewEntry.Length = NewEntry.MaximumLength = Console->LineSize * sizeof(WCHAR);
150 // NewEntry.Buffer = Console->LineBuffer;
151
152 /* Don't add blank or duplicate entries */
153 if (Entry->Length == 0 || Hist->MaxEntries == 0 ||
154 (Hist->NumEntries > 0 &&
155 RtlEqualUnicodeString(&Hist->Entries[Hist->NumEntries - 1], Entry, FALSE)))
156 {
157 return;
158 }
159
160 if (Console->HistoryNoDup)
161 {
162 INT i;
163
164 /* Check if this line has been entered before */
165 for (i = Hist->NumEntries - 1; i >= 0; i--)
166 {
167 if (RtlEqualUnicodeString(&Hist->Entries[i], Entry, FALSE))
168 {
169 UNICODE_STRING NewEntry;
170
171 /* Just rotate the list to bring this entry to the end */
172 NewEntry = Hist->Entries[i];
173 memmove(&Hist->Entries[i], &Hist->Entries[i + 1],
174 (Hist->NumEntries - (i + 1)) * sizeof(UNICODE_STRING));
175 Hist->Entries[Hist->NumEntries - 1] = NewEntry;
176 Hist->Position = Hist->NumEntries - 1;
177 return;
178 }
179 }
180 }
181
182 if (Hist->NumEntries == Hist->MaxEntries)
183 {
184 /* List is full, remove oldest entry */
185 RtlFreeUnicodeString(&Hist->Entries[0]);
186 memmove(&Hist->Entries[0], &Hist->Entries[1],
187 --Hist->NumEntries * sizeof(UNICODE_STRING));
188 }
189
190 if (NT_SUCCESS(RtlDuplicateUnicodeString(0, Entry, &Hist->Entries[Hist->NumEntries])))
191 Hist->NumEntries++;
192 Hist->Position = Hist->NumEntries - 1;
193 }
194
195 VOID
196 HistoryGetCurrentEntry(PCONSRV_CONSOLE Console,
197 PUNICODE_STRING ExeName,
198 PUNICODE_STRING Entry)
199 {
200 PHISTORY_BUFFER Hist = HistoryCurrentBuffer(Console, ExeName);
201
202 if (!Hist || Hist->NumEntries == 0)
203 Entry->Length = 0;
204 else
205 *Entry = Hist->Entries[Hist->Position];
206 }
207
208 BOOL
209 HistoryRecallHistory(PCONSRV_CONSOLE Console,
210 PUNICODE_STRING ExeName,
211 INT Offset,
212 PUNICODE_STRING Entry)
213 {
214 PHISTORY_BUFFER Hist = HistoryCurrentBuffer(Console, ExeName);
215 ULONG Position = 0;
216
217 if (!Hist || Hist->NumEntries == 0) return FALSE;
218
219 Position = Hist->Position + Offset;
220 Position = min(max(Position, 0), Hist->NumEntries - 1);
221 Hist->Position = Position;
222
223 *Entry = Hist->Entries[Hist->Position];
224 return TRUE;
225 }
226
227 BOOL
228 HistoryFindEntryByPrefix(PCONSRV_CONSOLE Console,
229 PUNICODE_STRING ExeName,
230 PUNICODE_STRING Prefix,
231 PUNICODE_STRING Entry)
232 {
233 INT HistPos;
234
235 /* Search for history entries starting with input. */
236 PHISTORY_BUFFER Hist = HistoryCurrentBuffer(Console, ExeName);
237 if (!Hist || Hist->NumEntries == 0) return FALSE;
238
239 /*
240 * Like Up/F5, on first time start from current (usually last) entry,
241 * but on subsequent times start at previous entry.
242 */
243 if (Console->LineUpPressed)
244 Hist->Position = (Hist->Position ? Hist->Position : Hist->NumEntries) - 1;
245 Console->LineUpPressed = TRUE;
246
247 // Entry.Length = Console->LinePos * sizeof(WCHAR); // == Pos * sizeof(WCHAR)
248 // Entry.Buffer = Console->LineBuffer;
249
250 /*
251 * Keep going backwards, even wrapping around to the end,
252 * until we get back to starting point.
253 */
254 HistPos = Hist->Position;
255 do
256 {
257 if (RtlPrefixUnicodeString(Prefix, &Hist->Entries[HistPos], FALSE))
258 {
259 Hist->Position = HistPos;
260 *Entry = Hist->Entries[HistPos];
261 return TRUE;
262 }
263 if (--HistPos < 0) HistPos += Hist->NumEntries;
264 } while (HistPos != Hist->Position);
265
266 return FALSE;
267 }
268
269 VOID
270 HistoryDeleteCurrentBuffer(PCONSRV_CONSOLE Console,
271 PVOID ExeName)
272 {
273 HistoryDeleteBuffer(HistoryCurrentBuffer(Console, ExeName));
274 }
275
276 VOID
277 HistoryDeleteBuffers(PCONSRV_CONSOLE Console)
278 {
279 PLIST_ENTRY CurrentEntry;
280 PHISTORY_BUFFER HistoryBuffer;
281
282 while (!IsListEmpty(&Console->HistoryBuffers))
283 {
284 CurrentEntry = RemoveHeadList(&Console->HistoryBuffers);
285 HistoryBuffer = CONTAINING_RECORD(CurrentEntry, HISTORY_BUFFER, ListEntry);
286 HistoryDeleteBuffer(HistoryBuffer);
287 }
288 }
289
290
291 /* PUBLIC SERVER APIS *********************************************************/
292
293 CSR_API(SrvGetConsoleCommandHistory)
294 {
295 NTSTATUS Status;
296 PCONSOLE_GETCOMMANDHISTORY GetCommandHistoryRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetCommandHistoryRequest;
297 PCONSRV_CONSOLE Console;
298 ULONG BytesWritten = 0;
299 PHISTORY_BUFFER Hist;
300
301 DPRINT1("SrvGetConsoleCommandHistory entered\n");
302
303 if ( !CsrValidateMessageBuffer(ApiMessage,
304 (PVOID*)&GetCommandHistoryRequest->History,
305 GetCommandHistoryRequest->HistoryLength,
306 sizeof(BYTE)) ||
307 !CsrValidateMessageBuffer(ApiMessage,
308 (PVOID*)&GetCommandHistoryRequest->ExeName,
309 GetCommandHistoryRequest->ExeLength,
310 sizeof(BYTE)) )
311 {
312 return STATUS_INVALID_PARAMETER;
313 }
314
315 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
316 if (!NT_SUCCESS(Status)) return Status;
317
318 Hist = HistoryFindBuffer(Console,
319 GetCommandHistoryRequest->ExeName,
320 GetCommandHistoryRequest->ExeLength,
321 GetCommandHistoryRequest->Unicode2);
322 if (Hist)
323 {
324 ULONG i;
325
326 LPSTR TargetBufferA;
327 LPWSTR TargetBufferW;
328 ULONG BufferSize = GetCommandHistoryRequest->HistoryLength;
329
330 ULONG Offset = 0;
331 ULONG SourceLength;
332
333 if (GetCommandHistoryRequest->Unicode)
334 {
335 TargetBufferW = GetCommandHistoryRequest->History;
336 BufferSize /= sizeof(WCHAR);
337 }
338 else
339 {
340 TargetBufferA = GetCommandHistoryRequest->History;
341 }
342
343 for (i = 0; i < Hist->NumEntries; i++)
344 {
345 SourceLength = Hist->Entries[i].Length / sizeof(WCHAR);
346 if (Offset + SourceLength + 1 > BufferSize)
347 {
348 Status = STATUS_BUFFER_OVERFLOW;
349 break;
350 }
351
352 if (GetCommandHistoryRequest->Unicode)
353 {
354 RtlCopyMemory(&TargetBufferW[Offset], Hist->Entries[i].Buffer, SourceLength * sizeof(WCHAR));
355 Offset += SourceLength;
356 TargetBufferW[Offset++] = L'\0';
357 }
358 else
359 {
360 ConvertInputUnicodeToAnsi(Console,
361 Hist->Entries[i].Buffer, SourceLength * sizeof(WCHAR),
362 &TargetBufferA[Offset], SourceLength);
363 Offset += SourceLength;
364 TargetBufferA[Offset++] = '\0';
365 }
366 }
367
368 if (GetCommandHistoryRequest->Unicode)
369 BytesWritten = Offset * sizeof(WCHAR);
370 else
371 BytesWritten = Offset;
372 }
373
374 // GetCommandHistoryRequest->HistoryLength = TargetBuffer - (PBYTE)GetCommandHistoryRequest->History;
375 GetCommandHistoryRequest->HistoryLength = BytesWritten;
376
377 ConSrvReleaseConsole(Console, TRUE);
378 return Status;
379 }
380
381 CSR_API(SrvGetConsoleCommandHistoryLength)
382 {
383 NTSTATUS Status;
384 PCONSOLE_GETCOMMANDHISTORYLENGTH GetCommandHistoryLengthRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetCommandHistoryLengthRequest;
385 PCONSRV_CONSOLE Console;
386 PHISTORY_BUFFER Hist;
387 ULONG Length = 0;
388
389 if (!CsrValidateMessageBuffer(ApiMessage,
390 (PVOID*)&GetCommandHistoryLengthRequest->ExeName,
391 GetCommandHistoryLengthRequest->ExeLength,
392 sizeof(BYTE)))
393 {
394 return STATUS_INVALID_PARAMETER;
395 }
396
397 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
398 if (!NT_SUCCESS(Status)) return Status;
399
400 Hist = HistoryFindBuffer(Console,
401 GetCommandHistoryLengthRequest->ExeName,
402 GetCommandHistoryLengthRequest->ExeLength,
403 GetCommandHistoryLengthRequest->Unicode2);
404 if (Hist)
405 {
406 ULONG i;
407 for (i = 0; i < Hist->NumEntries; i++)
408 Length += Hist->Entries[i].Length + sizeof(WCHAR); // Each entry is returned NULL-terminated
409 }
410 /*
411 * Quick and dirty way of getting the number of bytes of the
412 * corresponding ANSI string from the one in UNICODE.
413 */
414 if (!GetCommandHistoryLengthRequest->Unicode)
415 Length /= sizeof(WCHAR);
416
417 GetCommandHistoryLengthRequest->HistoryLength = Length;
418
419 ConSrvReleaseConsole(Console, TRUE);
420 return Status;
421 }
422
423 CSR_API(SrvExpungeConsoleCommandHistory)
424 {
425 NTSTATUS Status;
426 PCONSOLE_EXPUNGECOMMANDHISTORY ExpungeCommandHistoryRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ExpungeCommandHistoryRequest;
427 PCONSRV_CONSOLE Console;
428 PHISTORY_BUFFER Hist;
429
430 if (!CsrValidateMessageBuffer(ApiMessage,
431 (PVOID*)&ExpungeCommandHistoryRequest->ExeName,
432 ExpungeCommandHistoryRequest->ExeLength,
433 sizeof(BYTE)))
434 {
435 return STATUS_INVALID_PARAMETER;
436 }
437
438 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
439 if (!NT_SUCCESS(Status)) return Status;
440
441 Hist = HistoryFindBuffer(Console,
442 ExpungeCommandHistoryRequest->ExeName,
443 ExpungeCommandHistoryRequest->ExeLength,
444 ExpungeCommandHistoryRequest->Unicode2);
445 HistoryDeleteBuffer(Hist);
446
447 ConSrvReleaseConsole(Console, TRUE);
448 return Status;
449 }
450
451 CSR_API(SrvSetConsoleNumberOfCommands)
452 {
453 NTSTATUS Status;
454 PCONSOLE_SETHISTORYNUMBERCOMMANDS SetHistoryNumberCommandsRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.SetHistoryNumberCommandsRequest;
455 PCONSRV_CONSOLE Console;
456 PHISTORY_BUFFER Hist;
457
458 if (!CsrValidateMessageBuffer(ApiMessage,
459 (PVOID*)&SetHistoryNumberCommandsRequest->ExeName,
460 SetHistoryNumberCommandsRequest->ExeLength,
461 sizeof(BYTE)))
462 {
463 return STATUS_INVALID_PARAMETER;
464 }
465
466 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
467 if (!NT_SUCCESS(Status)) return Status;
468
469 Hist = HistoryFindBuffer(Console,
470 SetHistoryNumberCommandsRequest->ExeName,
471 SetHistoryNumberCommandsRequest->ExeLength,
472 SetHistoryNumberCommandsRequest->Unicode2);
473 if (Hist)
474 {
475 ULONG MaxEntries = SetHistoryNumberCommandsRequest->NumCommands;
476 PUNICODE_STRING OldEntryList = Hist->Entries;
477 PUNICODE_STRING NewEntryList = ConsoleAllocHeap(0, MaxEntries * sizeof(UNICODE_STRING));
478 if (!NewEntryList)
479 {
480 Status = STATUS_NO_MEMORY;
481 }
482 else
483 {
484 /* If necessary, shrink by removing oldest entries */
485 for (; Hist->NumEntries > MaxEntries; Hist->NumEntries--)
486 {
487 RtlFreeUnicodeString(Hist->Entries++);
488 Hist->Position += (Hist->Position == 0);
489 }
490
491 Hist->MaxEntries = MaxEntries;
492 Hist->Entries = memcpy(NewEntryList, Hist->Entries,
493 Hist->NumEntries * sizeof(UNICODE_STRING));
494 ConsoleFreeHeap(OldEntryList);
495 }
496 }
497
498 ConSrvReleaseConsole(Console, TRUE);
499 return Status;
500 }
501
502 CSR_API(SrvGetConsoleHistory)
503 {
504 #if 0 // Vista+
505 PCONSOLE_GETSETHISTORYINFO HistoryInfoRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.HistoryInfoRequest;
506 PCONSRV_CONSOLE Console;
507 NTSTATUS Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
508 if (NT_SUCCESS(Status))
509 {
510 HistoryInfoRequest->HistoryBufferSize = Console->HistoryBufferSize;
511 HistoryInfoRequest->NumberOfHistoryBuffers = Console->NumberOfHistoryBuffers;
512 HistoryInfoRequest->dwFlags = Console->HistoryNoDup;
513 ConSrvReleaseConsole(Console, TRUE);
514 }
515 return Status;
516 #else
517 DPRINT1("%s not yet implemented\n", __FUNCTION__);
518 return STATUS_NOT_IMPLEMENTED;
519 #endif
520 }
521
522 CSR_API(SrvSetConsoleHistory)
523 {
524 #if 0 // Vista+
525 PCONSOLE_GETSETHISTORYINFO HistoryInfoRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.HistoryInfoRequest;
526 PCONSRV_CONSOLE Console;
527 NTSTATUS Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
528 if (NT_SUCCESS(Status))
529 {
530 Console->HistoryBufferSize = HistoryInfoRequest->HistoryBufferSize;
531 Console->NumberOfHistoryBuffers = HistoryInfoRequest->NumberOfHistoryBuffers;
532 Console->HistoryNoDup = HistoryInfoRequest->dwFlags & HISTORY_NO_DUP_FLAG;
533 ConSrvReleaseConsole(Console, TRUE);
534 }
535 return Status;
536 #else
537 DPRINT1("%s not yet implemented\n", __FUNCTION__);
538 return STATUS_NOT_IMPLEMENTED;
539 #endif
540 }
541
542 CSR_API(SrvSetConsoleCommandHistoryMode)
543 {
544 NTSTATUS Status;
545 PCONSOLE_SETHISTORYMODE SetHistoryModeRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.SetHistoryModeRequest;
546 PCONSRV_CONSOLE Console;
547
548 DPRINT1("SrvSetConsoleCommandHistoryMode(Mode = %d) is not yet implemented\n",
549 SetHistoryModeRequest->Mode);
550
551 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
552 if (!NT_SUCCESS(Status)) return Status;
553
554 /* This API is not yet implemented */
555 Status = STATUS_NOT_IMPLEMENTED;
556
557 ConSrvReleaseConsole(Console, TRUE);
558 return Status;
559 }
560
561 /* EOF */