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 *******************************************************************/
17 typedef struct _HISTORY_BUFFER
23 UNICODE_STRING ExeName
;
24 PUNICODE_STRING Entries
;
25 } HISTORY_BUFFER
, *PHISTORY_BUFFER
;
29 ConvertInputAnsiToUnicode(PCONSRV_CONSOLE Console
,
34 PUSHORT TargetLength
);
36 ConvertInputUnicodeToAnsi(PCONSRV_CONSOLE Console
,
41 /*P*/USHORT TargetLength
);
44 /* PRIVATE FUNCTIONS **********************************************************/
46 static PHISTORY_BUFFER
47 HistoryCurrentBuffer(PCONSRV_CONSOLE Console
,
48 PUNICODE_STRING ExeName
)
50 PLIST_ENTRY Entry
= Console
->HistoryBuffers
.Flink
;
53 for (; Entry
!= &Console
->HistoryBuffers
; Entry
= Entry
->Flink
)
55 Hist
= CONTAINING_RECORD(Entry
, HISTORY_BUFFER
, ListEntry
);
56 if (RtlEqualUnicodeString(ExeName
, &Hist
->ExeName
, FALSE
))
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
;
65 Hist
->Entries
= ConsoleAllocHeap(0, Hist
->MaxEntries
* sizeof(UNICODE_STRING
));
68 ConsoleFreeHeap(Hist
);
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
);
78 static PHISTORY_BUFFER
79 HistoryFindBuffer(PCONSRV_CONSOLE Console
,
84 UNICODE_STRING ExeNameU
;
87 PHISTORY_BUFFER Hist
= NULL
;
89 if (ExeName
== NULL
) return NULL
;
93 ExeNameU
.Buffer
= ExeName
;
94 /* Length is in bytes */
95 ExeNameU
.MaximumLength
= ExeLength
;
99 if (!ConvertInputAnsiToUnicode(Console
,
101 &ExeNameU
.Buffer
, &ExeNameU
.MaximumLength
))
106 ExeNameU
.Length
= ExeNameU
.MaximumLength
;
108 Entry
= Console
->HistoryBuffers
.Flink
;
109 while (Entry
!= &Console
->HistoryBuffers
)
111 Hist
= CONTAINING_RECORD(Entry
, HISTORY_BUFFER
, ListEntry
);
113 /* For the history APIs, the caller is allowed to give only part of the name */
114 if (RtlPrefixUnicodeString(&ExeNameU
, &Hist
->ExeName
, TRUE
))
116 if (!UnicodeExe
) ConsoleFreeHeap(ExeNameU
.Buffer
);
120 Entry
= Entry
->Flink
;
123 if (!UnicodeExe
) ConsoleFreeHeap(ExeNameU
.Buffer
);
128 HistoryDeleteBuffer(PHISTORY_BUFFER Hist
)
132 while (Hist
->NumEntries
!= 0)
133 RtlFreeUnicodeString(&Hist
->Entries
[--Hist
->NumEntries
]);
135 ConsoleFreeHeap(Hist
->Entries
);
136 RemoveEntryList(&Hist
->ListEntry
);
137 ConsoleFreeHeap(Hist
);
141 HistoryAddEntry(PCONSRV_CONSOLE Console
,
142 PUNICODE_STRING ExeName
,
143 PUNICODE_STRING Entry
)
145 // UNICODE_STRING NewEntry;
146 PHISTORY_BUFFER Hist
= HistoryCurrentBuffer(Console
, ExeName
);
150 // NewEntry.Length = NewEntry.MaximumLength = Console->LineSize * sizeof(WCHAR);
151 // NewEntry.Buffer = Console->LineBuffer;
153 /* Don't add blank or duplicate entries */
154 if (Entry
->Length
== 0 || Hist
->MaxEntries
== 0 ||
155 (Hist
->NumEntries
> 0 &&
156 RtlEqualUnicodeString(&Hist
->Entries
[Hist
->NumEntries
- 1], Entry
, FALSE
)))
161 if (Console
->HistoryNoDup
)
165 /* Check if this line has been entered before */
166 for (i
= Hist
->NumEntries
- 1; i
>= 0; i
--)
168 if (RtlEqualUnicodeString(&Hist
->Entries
[i
], Entry
, FALSE
))
170 UNICODE_STRING NewEntry
;
172 /* Just rotate the list to bring this entry to the end */
173 NewEntry
= Hist
->Entries
[i
];
174 memmove(&Hist
->Entries
[i
], &Hist
->Entries
[i
+ 1],
175 (Hist
->NumEntries
- (i
+ 1)) * sizeof(UNICODE_STRING
));
176 Hist
->Entries
[Hist
->NumEntries
- 1] = NewEntry
;
177 Hist
->Position
= Hist
->NumEntries
- 1;
183 if (Hist
->NumEntries
== Hist
->MaxEntries
)
185 /* List is full, remove oldest entry */
186 RtlFreeUnicodeString(&Hist
->Entries
[0]);
187 memmove(&Hist
->Entries
[0], &Hist
->Entries
[1],
188 --Hist
->NumEntries
* sizeof(UNICODE_STRING
));
191 if (NT_SUCCESS(RtlDuplicateUnicodeString(0, Entry
, &Hist
->Entries
[Hist
->NumEntries
])))
193 Hist
->Position
= Hist
->NumEntries
- 1;
197 HistoryGetCurrentEntry(PCONSRV_CONSOLE Console
,
198 PUNICODE_STRING ExeName
,
199 PUNICODE_STRING Entry
)
201 PHISTORY_BUFFER Hist
= HistoryCurrentBuffer(Console
, ExeName
);
203 if (!Hist
|| Hist
->NumEntries
== 0)
206 *Entry
= Hist
->Entries
[Hist
->Position
];
210 HistoryRecallHistory(PCONSRV_CONSOLE Console
,
211 PUNICODE_STRING ExeName
,
213 PUNICODE_STRING Entry
)
215 PHISTORY_BUFFER Hist
= HistoryCurrentBuffer(Console
, ExeName
);
218 if (!Hist
|| Hist
->NumEntries
== 0) return FALSE
;
220 Position
= Hist
->Position
+ Offset
;
221 Position
= min(max(Position
, 0), Hist
->NumEntries
- 1);
222 Hist
->Position
= Position
;
224 *Entry
= Hist
->Entries
[Hist
->Position
];
229 HistoryFindEntryByPrefix(PCONSRV_CONSOLE Console
,
230 PUNICODE_STRING ExeName
,
231 PUNICODE_STRING Prefix
,
232 PUNICODE_STRING Entry
)
236 /* Search for history entries starting with input. */
237 PHISTORY_BUFFER Hist
= HistoryCurrentBuffer(Console
, ExeName
);
238 if (!Hist
|| Hist
->NumEntries
== 0) return FALSE
;
241 * Like Up/F5, on first time start from current (usually last) entry,
242 * but on subsequent times start at previous entry.
244 if (Console
->LineUpPressed
)
245 Hist
->Position
= (Hist
->Position
? Hist
->Position
: Hist
->NumEntries
) - 1;
246 Console
->LineUpPressed
= TRUE
;
248 // Entry.Length = Console->LinePos * sizeof(WCHAR); // == Pos * sizeof(WCHAR)
249 // Entry.Buffer = Console->LineBuffer;
252 * Keep going backwards, even wrapping around to the end,
253 * until we get back to starting point.
255 HistPos
= Hist
->Position
;
258 if (RtlPrefixUnicodeString(Prefix
, &Hist
->Entries
[HistPos
], FALSE
))
260 Hist
->Position
= HistPos
;
261 *Entry
= Hist
->Entries
[HistPos
];
264 if (--HistPos
< 0) HistPos
+= Hist
->NumEntries
;
265 } while (HistPos
!= Hist
->Position
);
271 HistoryDisplayCurrentHistory(PCONSRV_CONSOLE Console
,
272 PUNICODE_STRING ExeName
)
274 PTEXTMODE_SCREEN_BUFFER ActiveBuffer
;
280 PHISTORY_BUFFER Hist
= HistoryCurrentBuffer(Console
, ExeName
);
282 if (!Hist
) return NULL
;
283 if (Hist
->NumEntries
== 0) return NULL
;
285 if (GetType(Console
->ActiveBuffer
) != TEXTMODE_BUFFER
) return NULL
;
286 ActiveBuffer
= (PTEXTMODE_SCREEN_BUFFER
)Console
->ActiveBuffer
;
291 /* Center the popup window on the screen */
292 xLeft
= ActiveBuffer
->ViewOrigin
.X
+ (ActiveBuffer
->ViewSize
.X
- Width
) / 2;
293 yTop
= ActiveBuffer
->ViewOrigin
.Y
+ (ActiveBuffer
->ViewSize
.Y
- Height
) / 2;
295 /* Create the popup */
296 Popup
= CreatePopupWindow(Console
, ActiveBuffer
,
297 xLeft
, yTop
, Width
, Height
);
298 if (Popup
== NULL
) return NULL
;
300 Popup
->PopupInputRoutine
= NULL
;
306 HistoryDeleteCurrentBuffer(PCONSRV_CONSOLE Console
,
307 PUNICODE_STRING ExeName
)
309 HistoryDeleteBuffer(HistoryCurrentBuffer(Console
, ExeName
));
313 HistoryDeleteBuffers(PCONSRV_CONSOLE Console
)
315 PLIST_ENTRY CurrentEntry
;
316 PHISTORY_BUFFER HistoryBuffer
;
318 while (!IsListEmpty(&Console
->HistoryBuffers
))
320 CurrentEntry
= RemoveHeadList(&Console
->HistoryBuffers
);
321 HistoryBuffer
= CONTAINING_RECORD(CurrentEntry
, HISTORY_BUFFER
, ListEntry
);
322 HistoryDeleteBuffer(HistoryBuffer
);
327 /* PUBLIC SERVER APIS *********************************************************/
329 CSR_API(SrvGetConsoleCommandHistory
)
332 PCONSOLE_GETCOMMANDHISTORY GetCommandHistoryRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.GetCommandHistoryRequest
;
333 PCONSRV_CONSOLE Console
;
334 ULONG BytesWritten
= 0;
335 PHISTORY_BUFFER Hist
;
337 DPRINT1("SrvGetConsoleCommandHistory entered\n");
339 if ( !CsrValidateMessageBuffer(ApiMessage
,
340 (PVOID
*)&GetCommandHistoryRequest
->History
,
341 GetCommandHistoryRequest
->HistoryLength
,
343 !CsrValidateMessageBuffer(ApiMessage
,
344 (PVOID
*)&GetCommandHistoryRequest
->ExeName
,
345 GetCommandHistoryRequest
->ExeLength
,
348 return STATUS_INVALID_PARAMETER
;
351 Status
= ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process
),
353 if (!NT_SUCCESS(Status
)) return Status
;
355 Hist
= HistoryFindBuffer(Console
,
356 GetCommandHistoryRequest
->ExeName
,
357 GetCommandHistoryRequest
->ExeLength
,
358 GetCommandHistoryRequest
->Unicode2
);
364 LPWSTR TargetBufferW
;
365 ULONG BufferSize
= GetCommandHistoryRequest
->HistoryLength
;
370 if (GetCommandHistoryRequest
->Unicode
)
372 TargetBufferW
= GetCommandHistoryRequest
->History
;
373 BufferSize
/= sizeof(WCHAR
);
377 TargetBufferA
= GetCommandHistoryRequest
->History
;
380 for (i
= 0; i
< Hist
->NumEntries
; i
++)
382 SourceLength
= Hist
->Entries
[i
].Length
/ sizeof(WCHAR
);
383 if (Offset
+ SourceLength
+ 1 > BufferSize
)
385 Status
= STATUS_BUFFER_OVERFLOW
;
389 if (GetCommandHistoryRequest
->Unicode
)
391 RtlCopyMemory(&TargetBufferW
[Offset
], Hist
->Entries
[i
].Buffer
, SourceLength
* sizeof(WCHAR
));
392 Offset
+= SourceLength
;
393 TargetBufferW
[Offset
++] = L
'\0';
397 ConvertInputUnicodeToAnsi(Console
,
398 Hist
->Entries
[i
].Buffer
, SourceLength
* sizeof(WCHAR
),
399 &TargetBufferA
[Offset
], SourceLength
);
400 Offset
+= SourceLength
;
401 TargetBufferA
[Offset
++] = '\0';
405 if (GetCommandHistoryRequest
->Unicode
)
406 BytesWritten
= Offset
* sizeof(WCHAR
);
408 BytesWritten
= Offset
;
411 // GetCommandHistoryRequest->HistoryLength = TargetBuffer - (PBYTE)GetCommandHistoryRequest->History;
412 GetCommandHistoryRequest
->HistoryLength
= BytesWritten
;
414 ConSrvReleaseConsole(Console
, TRUE
);
418 CSR_API(SrvGetConsoleCommandHistoryLength
)
421 PCONSOLE_GETCOMMANDHISTORYLENGTH GetCommandHistoryLengthRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.GetCommandHistoryLengthRequest
;
422 PCONSRV_CONSOLE Console
;
423 PHISTORY_BUFFER Hist
;
426 if (!CsrValidateMessageBuffer(ApiMessage
,
427 (PVOID
*)&GetCommandHistoryLengthRequest
->ExeName
,
428 GetCommandHistoryLengthRequest
->ExeLength
,
431 return STATUS_INVALID_PARAMETER
;
434 Status
= ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process
),
436 if (!NT_SUCCESS(Status
)) return Status
;
438 Hist
= HistoryFindBuffer(Console
,
439 GetCommandHistoryLengthRequest
->ExeName
,
440 GetCommandHistoryLengthRequest
->ExeLength
,
441 GetCommandHistoryLengthRequest
->Unicode2
);
445 for (i
= 0; i
< Hist
->NumEntries
; i
++)
446 Length
+= Hist
->Entries
[i
].Length
+ sizeof(WCHAR
); // Each entry is returned NULL-terminated
449 * Quick and dirty way of getting the number of bytes of the
450 * corresponding ANSI string from the one in UNICODE.
452 if (!GetCommandHistoryLengthRequest
->Unicode
)
453 Length
/= sizeof(WCHAR
);
455 GetCommandHistoryLengthRequest
->HistoryLength
= Length
;
457 ConSrvReleaseConsole(Console
, TRUE
);
461 CSR_API(SrvExpungeConsoleCommandHistory
)
464 PCONSOLE_EXPUNGECOMMANDHISTORY ExpungeCommandHistoryRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.ExpungeCommandHistoryRequest
;
465 PCONSRV_CONSOLE Console
;
466 PHISTORY_BUFFER Hist
;
468 if (!CsrValidateMessageBuffer(ApiMessage
,
469 (PVOID
*)&ExpungeCommandHistoryRequest
->ExeName
,
470 ExpungeCommandHistoryRequest
->ExeLength
,
473 return STATUS_INVALID_PARAMETER
;
476 Status
= ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process
),
478 if (!NT_SUCCESS(Status
)) return Status
;
480 Hist
= HistoryFindBuffer(Console
,
481 ExpungeCommandHistoryRequest
->ExeName
,
482 ExpungeCommandHistoryRequest
->ExeLength
,
483 ExpungeCommandHistoryRequest
->Unicode2
);
484 HistoryDeleteBuffer(Hist
);
486 ConSrvReleaseConsole(Console
, TRUE
);
490 CSR_API(SrvSetConsoleNumberOfCommands
)
493 PCONSOLE_SETHISTORYNUMBERCOMMANDS SetHistoryNumberCommandsRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.SetHistoryNumberCommandsRequest
;
494 PCONSRV_CONSOLE Console
;
495 PHISTORY_BUFFER Hist
;
497 if (!CsrValidateMessageBuffer(ApiMessage
,
498 (PVOID
*)&SetHistoryNumberCommandsRequest
->ExeName
,
499 SetHistoryNumberCommandsRequest
->ExeLength
,
502 return STATUS_INVALID_PARAMETER
;
505 Status
= ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process
),
507 if (!NT_SUCCESS(Status
)) return Status
;
509 Hist
= HistoryFindBuffer(Console
,
510 SetHistoryNumberCommandsRequest
->ExeName
,
511 SetHistoryNumberCommandsRequest
->ExeLength
,
512 SetHistoryNumberCommandsRequest
->Unicode2
);
515 ULONG MaxEntries
= SetHistoryNumberCommandsRequest
->NumCommands
;
516 PUNICODE_STRING OldEntryList
= Hist
->Entries
;
517 PUNICODE_STRING NewEntryList
= ConsoleAllocHeap(0, MaxEntries
* sizeof(UNICODE_STRING
));
520 Status
= STATUS_NO_MEMORY
;
524 /* If necessary, shrink by removing oldest entries */
525 for (; Hist
->NumEntries
> MaxEntries
; Hist
->NumEntries
--)
527 RtlFreeUnicodeString(Hist
->Entries
++);
528 Hist
->Position
+= (Hist
->Position
== 0);
531 Hist
->MaxEntries
= MaxEntries
;
532 Hist
->Entries
= memcpy(NewEntryList
, Hist
->Entries
,
533 Hist
->NumEntries
* sizeof(UNICODE_STRING
));
534 ConsoleFreeHeap(OldEntryList
);
538 ConSrvReleaseConsole(Console
, TRUE
);
542 CSR_API(SrvGetConsoleHistory
)
545 PCONSOLE_GETSETHISTORYINFO HistoryInfoRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.HistoryInfoRequest
;
546 PCONSRV_CONSOLE Console
;
547 NTSTATUS Status
= ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process
),
549 if (NT_SUCCESS(Status
))
551 HistoryInfoRequest
->HistoryBufferSize
= Console
->HistoryBufferSize
;
552 HistoryInfoRequest
->NumberOfHistoryBuffers
= Console
->NumberOfHistoryBuffers
;
553 HistoryInfoRequest
->dwFlags
= (Console
->HistoryNoDup
? HISTORY_NO_DUP_FLAG
: 0);
554 ConSrvReleaseConsole(Console
, TRUE
);
558 DPRINT1("%s not yet implemented\n", __FUNCTION__
);
559 return STATUS_NOT_IMPLEMENTED
;
563 CSR_API(SrvSetConsoleHistory
)
566 PCONSOLE_GETSETHISTORYINFO HistoryInfoRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.HistoryInfoRequest
;
567 PCONSRV_CONSOLE Console
;
568 NTSTATUS Status
= ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process
),
570 if (NT_SUCCESS(Status
))
572 Console
->HistoryBufferSize
= HistoryInfoRequest
->HistoryBufferSize
;
573 Console
->NumberOfHistoryBuffers
= HistoryInfoRequest
->NumberOfHistoryBuffers
;
574 Console
->HistoryNoDup
= !!(HistoryInfoRequest
->dwFlags
& HISTORY_NO_DUP_FLAG
);
575 ConSrvReleaseConsole(Console
, TRUE
);
579 DPRINT1("%s not yet implemented\n", __FUNCTION__
);
580 return STATUS_NOT_IMPLEMENTED
;
584 CSR_API(SrvSetConsoleCommandHistoryMode
)
587 PCONSOLE_SETHISTORYMODE SetHistoryModeRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.SetHistoryModeRequest
;
588 PCONSRV_CONSOLE Console
;
590 DPRINT("SrvSetConsoleCommandHistoryMode(Mode = %d) is not yet implemented\n",
591 SetHistoryModeRequest
->Mode
);
593 Status
= ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process
),
595 if (!NT_SUCCESS(Status
)) return Status
;
597 Console
->InsertMode
= !!(SetHistoryModeRequest
->Mode
& CONSOLE_OVERSTRIKE
);
599 ConSrvReleaseConsole(Console
, TRUE
);
600 return STATUS_SUCCESS
;