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
9 /* INCLUDES *******************************************************************/
16 typedef struct _HISTORY_BUFFER
22 UNICODE_STRING ExeName
;
23 PUNICODE_STRING Entries
;
24 } HISTORY_BUFFER
, *PHISTORY_BUFFER
;
28 ConvertInputAnsiToUnicode(PCONSOLE Console
,
33 PUSHORT TargetLength
);
35 ConvertInputUnicodeToAnsi(PCONSOLE Console
,
40 /*P*/USHORT TargetLength
);
43 /* PRIVATE FUNCTIONS **********************************************************/
45 static PHISTORY_BUFFER
46 HistoryCurrentBuffer(PCONSRV_CONSOLE Console
,
47 PUNICODE_STRING ExeName
)
49 PLIST_ENTRY Entry
= Console
->HistoryBuffers
.Flink
;
52 for (; Entry
!= &Console
->HistoryBuffers
; Entry
= Entry
->Flink
)
54 Hist
= CONTAINING_RECORD(Entry
, HISTORY_BUFFER
, ListEntry
);
55 if (RtlEqualUnicodeString(ExeName
, &Hist
->ExeName
, FALSE
))
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
;
64 Hist
->Entries
= ConsoleAllocHeap(0, Hist
->MaxEntries
* sizeof(UNICODE_STRING
));
67 ConsoleFreeHeap(Hist
);
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
);
77 static PHISTORY_BUFFER
78 HistoryFindBuffer(PCONSRV_CONSOLE Console
,
83 UNICODE_STRING ExeNameU
;
86 PHISTORY_BUFFER Hist
= NULL
;
88 if (ExeName
== NULL
) return NULL
;
92 ExeNameU
.Buffer
= ExeName
;
93 /* Length is in bytes */
94 ExeNameU
.MaximumLength
= ExeLength
;
98 if (!ConvertInputAnsiToUnicode(Console
,
100 &ExeNameU
.Buffer
, &ExeNameU
.MaximumLength
))
105 ExeNameU
.Length
= ExeNameU
.MaximumLength
;
107 Entry
= Console
->HistoryBuffers
.Flink
;
108 while (Entry
!= &Console
->HistoryBuffers
)
110 Hist
= CONTAINING_RECORD(Entry
, HISTORY_BUFFER
, ListEntry
);
112 /* For the history APIs, the caller is allowed to give only part of the name */
113 if (RtlPrefixUnicodeString(&ExeNameU
, &Hist
->ExeName
, TRUE
))
115 if (!UnicodeExe
) ConsoleFreeHeap(ExeNameU
.Buffer
);
119 Entry
= Entry
->Flink
;
122 if (!UnicodeExe
) ConsoleFreeHeap(ExeNameU
.Buffer
);
127 HistoryDeleteBuffer(PHISTORY_BUFFER Hist
)
131 while (Hist
->NumEntries
!= 0)
132 RtlFreeUnicodeString(&Hist
->Entries
[--Hist
->NumEntries
]);
134 ConsoleFreeHeap(Hist
->Entries
);
135 RemoveEntryList(&Hist
->ListEntry
);
136 ConsoleFreeHeap(Hist
);
140 HistoryAddEntry(PCONSRV_CONSOLE Console
,
141 PUNICODE_STRING ExeName
,
142 PUNICODE_STRING Entry
)
144 // UNICODE_STRING NewEntry;
145 PHISTORY_BUFFER Hist
= HistoryCurrentBuffer(Console
, ExeName
);
149 // NewEntry.Length = NewEntry.MaximumLength = Console->LineSize * sizeof(WCHAR);
150 // NewEntry.Buffer = Console->LineBuffer;
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
)))
160 if (Console
->HistoryNoDup
)
164 /* Check if this line has been entered before */
165 for (i
= Hist
->NumEntries
- 1; i
>= 0; i
--)
167 if (RtlEqualUnicodeString(&Hist
->Entries
[i
], Entry
, FALSE
))
169 UNICODE_STRING NewEntry
;
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;
182 if (Hist
->NumEntries
== Hist
->MaxEntries
)
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
));
190 if (NT_SUCCESS(RtlDuplicateUnicodeString(0, Entry
, &Hist
->Entries
[Hist
->NumEntries
])))
192 Hist
->Position
= Hist
->NumEntries
- 1;
196 HistoryGetCurrentEntry(PCONSRV_CONSOLE Console
,
197 PUNICODE_STRING ExeName
,
198 PUNICODE_STRING Entry
)
200 PHISTORY_BUFFER Hist
= HistoryCurrentBuffer(Console
, ExeName
);
202 if (!Hist
|| Hist
->NumEntries
== 0)
205 *Entry
= Hist
->Entries
[Hist
->Position
];
209 HistoryRecallHistory(PCONSRV_CONSOLE Console
,
210 PUNICODE_STRING ExeName
,
212 PUNICODE_STRING Entry
)
214 PHISTORY_BUFFER Hist
= HistoryCurrentBuffer(Console
, ExeName
);
217 if (!Hist
|| Hist
->NumEntries
== 0) return FALSE
;
219 Position
= Hist
->Position
+ Offset
;
220 Position
= min(max(Position
, 0), Hist
->NumEntries
- 1);
221 Hist
->Position
= Position
;
223 *Entry
= Hist
->Entries
[Hist
->Position
];
228 HistoryFindEntryByPrefix(PCONSRV_CONSOLE Console
,
229 PUNICODE_STRING ExeName
,
230 PUNICODE_STRING Prefix
,
231 PUNICODE_STRING Entry
)
235 /* Search for history entries starting with input. */
236 PHISTORY_BUFFER Hist
= HistoryCurrentBuffer(Console
, ExeName
);
237 if (!Hist
|| Hist
->NumEntries
== 0) return FALSE
;
240 * Like Up/F5, on first time start from current (usually last) entry,
241 * but on subsequent times start at previous entry.
243 if (Console
->LineUpPressed
)
244 Hist
->Position
= (Hist
->Position
? Hist
->Position
: Hist
->NumEntries
) - 1;
245 Console
->LineUpPressed
= TRUE
;
247 // Entry.Length = Console->LinePos * sizeof(WCHAR); // == Pos * sizeof(WCHAR)
248 // Entry.Buffer = Console->LineBuffer;
251 * Keep going backwards, even wrapping around to the end,
252 * until we get back to starting point.
254 HistPos
= Hist
->Position
;
257 if (RtlPrefixUnicodeString(Prefix
, &Hist
->Entries
[HistPos
], FALSE
))
259 Hist
->Position
= HistPos
;
260 *Entry
= Hist
->Entries
[HistPos
];
263 if (--HistPos
< 0) HistPos
+= Hist
->NumEntries
;
264 } while (HistPos
!= Hist
->Position
);
270 HistoryDeleteCurrentBuffer(PCONSRV_CONSOLE Console
,
273 HistoryDeleteBuffer(HistoryCurrentBuffer(Console
, ExeName
));
277 HistoryDeleteBuffers(PCONSRV_CONSOLE Console
)
279 PLIST_ENTRY CurrentEntry
;
280 PHISTORY_BUFFER HistoryBuffer
;
282 while (!IsListEmpty(&Console
->HistoryBuffers
))
284 CurrentEntry
= RemoveHeadList(&Console
->HistoryBuffers
);
285 HistoryBuffer
= CONTAINING_RECORD(CurrentEntry
, HISTORY_BUFFER
, ListEntry
);
286 HistoryDeleteBuffer(HistoryBuffer
);
291 /* PUBLIC SERVER APIS *********************************************************/
293 CSR_API(SrvGetConsoleCommandHistory
)
296 PCONSOLE_GETCOMMANDHISTORY GetCommandHistoryRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.GetCommandHistoryRequest
;
297 PCONSRV_CONSOLE Console
;
298 ULONG BytesWritten
= 0;
299 PHISTORY_BUFFER Hist
;
301 DPRINT1("SrvGetConsoleCommandHistory entered\n");
303 if ( !CsrValidateMessageBuffer(ApiMessage
,
304 (PVOID
*)&GetCommandHistoryRequest
->History
,
305 GetCommandHistoryRequest
->HistoryLength
,
307 !CsrValidateMessageBuffer(ApiMessage
,
308 (PVOID
*)&GetCommandHistoryRequest
->ExeName
,
309 GetCommandHistoryRequest
->ExeLength
,
312 return STATUS_INVALID_PARAMETER
;
315 Status
= ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process
), &Console
, TRUE
);
316 if (!NT_SUCCESS(Status
)) return Status
;
318 Hist
= HistoryFindBuffer(Console
,
319 GetCommandHistoryRequest
->ExeName
,
320 GetCommandHistoryRequest
->ExeLength
,
321 GetCommandHistoryRequest
->Unicode2
);
327 LPWSTR TargetBufferW
;
328 ULONG BufferSize
= GetCommandHistoryRequest
->HistoryLength
;
333 if (GetCommandHistoryRequest
->Unicode
)
335 TargetBufferW
= GetCommandHistoryRequest
->History
;
336 BufferSize
/= sizeof(WCHAR
);
340 TargetBufferA
= GetCommandHistoryRequest
->History
;
343 for (i
= 0; i
< Hist
->NumEntries
; i
++)
345 SourceLength
= Hist
->Entries
[i
].Length
/ sizeof(WCHAR
);
346 if (Offset
+ SourceLength
+ 1 > BufferSize
)
348 Status
= STATUS_BUFFER_OVERFLOW
;
352 if (GetCommandHistoryRequest
->Unicode
)
354 RtlCopyMemory(&TargetBufferW
[Offset
], Hist
->Entries
[i
].Buffer
, SourceLength
* sizeof(WCHAR
));
355 Offset
+= SourceLength
;
356 TargetBufferW
[Offset
++] = L
'\0';
360 ConvertInputUnicodeToAnsi(Console
,
361 Hist
->Entries
[i
].Buffer
, SourceLength
* sizeof(WCHAR
),
362 &TargetBufferA
[Offset
], SourceLength
);
363 Offset
+= SourceLength
;
364 TargetBufferA
[Offset
++] = '\0';
368 if (GetCommandHistoryRequest
->Unicode
)
369 BytesWritten
= Offset
* sizeof(WCHAR
);
371 BytesWritten
= Offset
;
374 // GetCommandHistoryRequest->HistoryLength = TargetBuffer - (PBYTE)GetCommandHistoryRequest->History;
375 GetCommandHistoryRequest
->HistoryLength
= BytesWritten
;
377 ConSrvReleaseConsole(Console
, TRUE
);
381 CSR_API(SrvGetConsoleCommandHistoryLength
)
384 PCONSOLE_GETCOMMANDHISTORYLENGTH GetCommandHistoryLengthRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.GetCommandHistoryLengthRequest
;
385 PCONSRV_CONSOLE Console
;
386 PHISTORY_BUFFER Hist
;
389 if (!CsrValidateMessageBuffer(ApiMessage
,
390 (PVOID
*)&GetCommandHistoryLengthRequest
->ExeName
,
391 GetCommandHistoryLengthRequest
->ExeLength
,
394 return STATUS_INVALID_PARAMETER
;
397 Status
= ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process
), &Console
, TRUE
);
398 if (!NT_SUCCESS(Status
)) return Status
;
400 Hist
= HistoryFindBuffer(Console
,
401 GetCommandHistoryLengthRequest
->ExeName
,
402 GetCommandHistoryLengthRequest
->ExeLength
,
403 GetCommandHistoryLengthRequest
->Unicode2
);
407 for (i
= 0; i
< Hist
->NumEntries
; i
++)
408 Length
+= Hist
->Entries
[i
].Length
+ sizeof(WCHAR
); // Each entry is returned NULL-terminated
411 * Quick and dirty way of getting the number of bytes of the
412 * corresponding ANSI string from the one in UNICODE.
414 if (!GetCommandHistoryLengthRequest
->Unicode
)
415 Length
/= sizeof(WCHAR
);
417 GetCommandHistoryLengthRequest
->HistoryLength
= Length
;
419 ConSrvReleaseConsole(Console
, TRUE
);
423 CSR_API(SrvExpungeConsoleCommandHistory
)
426 PCONSOLE_EXPUNGECOMMANDHISTORY ExpungeCommandHistoryRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.ExpungeCommandHistoryRequest
;
427 PCONSRV_CONSOLE Console
;
428 PHISTORY_BUFFER Hist
;
430 if (!CsrValidateMessageBuffer(ApiMessage
,
431 (PVOID
*)&ExpungeCommandHistoryRequest
->ExeName
,
432 ExpungeCommandHistoryRequest
->ExeLength
,
435 return STATUS_INVALID_PARAMETER
;
438 Status
= ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process
), &Console
, TRUE
);
439 if (!NT_SUCCESS(Status
)) return Status
;
441 Hist
= HistoryFindBuffer(Console
,
442 ExpungeCommandHistoryRequest
->ExeName
,
443 ExpungeCommandHistoryRequest
->ExeLength
,
444 ExpungeCommandHistoryRequest
->Unicode2
);
445 HistoryDeleteBuffer(Hist
);
447 ConSrvReleaseConsole(Console
, TRUE
);
451 CSR_API(SrvSetConsoleNumberOfCommands
)
454 PCONSOLE_SETHISTORYNUMBERCOMMANDS SetHistoryNumberCommandsRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.SetHistoryNumberCommandsRequest
;
455 PCONSRV_CONSOLE Console
;
456 PHISTORY_BUFFER Hist
;
458 if (!CsrValidateMessageBuffer(ApiMessage
,
459 (PVOID
*)&SetHistoryNumberCommandsRequest
->ExeName
,
460 SetHistoryNumberCommandsRequest
->ExeLength
,
463 return STATUS_INVALID_PARAMETER
;
466 Status
= ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process
), &Console
, TRUE
);
467 if (!NT_SUCCESS(Status
)) return Status
;
469 Hist
= HistoryFindBuffer(Console
,
470 SetHistoryNumberCommandsRequest
->ExeName
,
471 SetHistoryNumberCommandsRequest
->ExeLength
,
472 SetHistoryNumberCommandsRequest
->Unicode2
);
475 ULONG MaxEntries
= SetHistoryNumberCommandsRequest
->NumCommands
;
476 PUNICODE_STRING OldEntryList
= Hist
->Entries
;
477 PUNICODE_STRING NewEntryList
= ConsoleAllocHeap(0, MaxEntries
* sizeof(UNICODE_STRING
));
480 Status
= STATUS_NO_MEMORY
;
484 /* If necessary, shrink by removing oldest entries */
485 for (; Hist
->NumEntries
> MaxEntries
; Hist
->NumEntries
--)
487 RtlFreeUnicodeString(Hist
->Entries
++);
488 Hist
->Position
+= (Hist
->Position
== 0);
491 Hist
->MaxEntries
= MaxEntries
;
492 Hist
->Entries
= memcpy(NewEntryList
, Hist
->Entries
,
493 Hist
->NumEntries
* sizeof(UNICODE_STRING
));
494 ConsoleFreeHeap(OldEntryList
);
498 ConSrvReleaseConsole(Console
, TRUE
);
502 CSR_API(SrvGetConsoleHistory
)
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
))
510 HistoryInfoRequest
->HistoryBufferSize
= Console
->HistoryBufferSize
;
511 HistoryInfoRequest
->NumberOfHistoryBuffers
= Console
->NumberOfHistoryBuffers
;
512 HistoryInfoRequest
->dwFlags
= Console
->HistoryNoDup
;
513 ConSrvReleaseConsole(Console
, TRUE
);
517 DPRINT1("%s not yet implemented\n", __FUNCTION__
);
518 return STATUS_NOT_IMPLEMENTED
;
522 CSR_API(SrvSetConsoleHistory
)
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
))
530 Console
->HistoryBufferSize
= HistoryInfoRequest
->HistoryBufferSize
;
531 Console
->NumberOfHistoryBuffers
= HistoryInfoRequest
->NumberOfHistoryBuffers
;
532 Console
->HistoryNoDup
= HistoryInfoRequest
->dwFlags
& HISTORY_NO_DUP_FLAG
;
533 ConSrvReleaseConsole(Console
, TRUE
);
537 DPRINT1("%s not yet implemented\n", __FUNCTION__
);
538 return STATUS_NOT_IMPLEMENTED
;
542 CSR_API(SrvSetConsoleCommandHistoryMode
)
545 PCONSOLE_SETHISTORYMODE SetHistoryModeRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.SetHistoryModeRequest
;
546 PCONSRV_CONSOLE Console
;
548 DPRINT1("SrvSetConsoleCommandHistoryMode(Mode = %d) is not yet implemented\n",
549 SetHistoryModeRequest
->Mode
);
551 Status
= ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process
), &Console
, TRUE
);
552 if (!NT_SUCCESS(Status
)) return Status
;
554 /* This API is not yet implemented */
555 Status
= STATUS_NOT_IMPLEMENTED
;
557 ConSrvReleaseConsole(Console
, TRUE
);