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 #include "popup.h" 13 14 #define NDEBUG 15 #include <debug.h> 16 17 typedef struct _HISTORY_BUFFER 18 { 19 LIST_ENTRY ListEntry; 20 ULONG Position; 21 ULONG MaxEntries; 22 ULONG NumEntries; 23 UNICODE_STRING ExeName; 24 PUNICODE_STRING Entries; 25 } HISTORY_BUFFER, *PHISTORY_BUFFER; 26 27 28 BOOLEAN 29 ConvertInputAnsiToUnicode(PCONSRV_CONSOLE Console, 30 PVOID Source, 31 USHORT SourceLength, 32 // BOOLEAN IsUnicode, 33 PWCHAR* Target, 34 PUSHORT TargetLength); 35 BOOLEAN 36 ConvertInputUnicodeToAnsi(PCONSRV_CONSOLE Console, 37 PVOID Source, 38 USHORT SourceLength, 39 // BOOLEAN IsAnsi, 40 PCHAR/* * */ Target, 41 /*P*/USHORT TargetLength); 42 43 44 /* PRIVATE FUNCTIONS **********************************************************/ 45 46 static PHISTORY_BUFFER 47 HistoryCurrentBuffer( 48 IN PCONSRV_CONSOLE Console, 49 IN PUNICODE_STRING ExeName) 50 { 51 PLIST_ENTRY Entry; 52 PHISTORY_BUFFER Hist; 53 54 for (Entry = Console->HistoryBuffers.Flink; 55 Entry != &Console->HistoryBuffers; 56 Entry = Entry->Flink) 57 { 58 Hist = CONTAINING_RECORD(Entry, HISTORY_BUFFER, ListEntry); 59 if (RtlEqualUnicodeString(ExeName, &Hist->ExeName, FALSE)) 60 return Hist; 61 } 62 63 /* Could not find the buffer, create a new one */ 64 65 if (Console->NumberOfHistoryBuffers < Console->MaxNumberOfHistoryBuffers) 66 { 67 Hist = ConsoleAllocHeap(0, sizeof(HISTORY_BUFFER) + ExeName->Length); 68 if (!Hist) return NULL; 69 Hist->MaxEntries = Console->HistoryBufferSize; 70 Hist->NumEntries = 0; 71 Hist->Entries = ConsoleAllocHeap(0, Hist->MaxEntries * sizeof(UNICODE_STRING)); 72 if (!Hist->Entries) 73 { 74 ConsoleFreeHeap(Hist); 75 return NULL; 76 } 77 Hist->ExeName.Length = Hist->ExeName.MaximumLength = ExeName->Length; 78 Hist->ExeName.Buffer = (PWCHAR)(Hist + 1); 79 RtlCopyMemory(Hist->ExeName.Buffer, ExeName->Buffer, ExeName->Length); 80 InsertHeadList(&Console->HistoryBuffers, &Hist->ListEntry); 81 Console->NumberOfHistoryBuffers++; 82 return Hist; 83 } 84 else 85 { 86 // FIXME: TODO !!!!!!! 87 // Reuse an older history buffer, if possible with the same Exe name, 88 // otherwise take the oldest one and reset its contents. 89 // And move the history buffer back to the head of the list. 90 UNIMPLEMENTED; 91 return NULL; 92 } 93 } 94 95 static PHISTORY_BUFFER 96 HistoryFindBuffer(PCONSRV_CONSOLE Console, 97 PVOID ExeName, 98 USHORT ExeLength, 99 BOOLEAN UnicodeExe) 100 { 101 UNICODE_STRING ExeNameU; 102 103 PLIST_ENTRY Entry; 104 PHISTORY_BUFFER Hist = NULL; 105 106 if (ExeName == NULL) return NULL; 107 108 if (UnicodeExe) 109 { 110 ExeNameU.Buffer = ExeName; 111 /* Length is in bytes */ 112 ExeNameU.MaximumLength = ExeLength; 113 } 114 else 115 { 116 if (!ConvertInputAnsiToUnicode(Console, 117 ExeName, ExeLength, 118 &ExeNameU.Buffer, &ExeNameU.MaximumLength)) 119 { 120 return NULL; 121 } 122 } 123 ExeNameU.Length = ExeNameU.MaximumLength; 124 125 for (Entry = Console->HistoryBuffers.Flink; 126 Entry != &Console->HistoryBuffers; 127 Entry = Entry->Flink) 128 { 129 Hist = CONTAINING_RECORD(Entry, HISTORY_BUFFER, ListEntry); 130 131 /* For the history APIs, the caller is allowed to give only part of the name */ 132 if (RtlPrefixUnicodeString(&ExeNameU, &Hist->ExeName, TRUE)) 133 { 134 if (!UnicodeExe) ConsoleFreeHeap(ExeNameU.Buffer); 135 return Hist; 136 } 137 } 138 139 if (!UnicodeExe) ConsoleFreeHeap(ExeNameU.Buffer); 140 return NULL; 141 } 142 143 static VOID 144 HistoryDeleteBuffer(PHISTORY_BUFFER Hist) 145 { 146 if (!Hist) return; 147 148 while (Hist->NumEntries != 0) 149 RtlFreeUnicodeString(&Hist->Entries[--Hist->NumEntries]); 150 151 ConsoleFreeHeap(Hist->Entries); 152 RemoveEntryList(&Hist->ListEntry); 153 ConsoleFreeHeap(Hist); 154 } 155 156 static NTSTATUS 157 HistoryResizeBuffer( 158 IN PHISTORY_BUFFER Hist, 159 IN ULONG NumCommands) 160 { 161 PUNICODE_STRING OldEntryList = Hist->Entries; 162 PUNICODE_STRING NewEntryList; 163 164 NewEntryList = ConsoleAllocHeap(0, NumCommands * sizeof(UNICODE_STRING)); 165 if (!NewEntryList) 166 return STATUS_NO_MEMORY; 167 168 /* If necessary, shrink by removing oldest entries */ 169 for (; Hist->NumEntries > NumCommands; Hist->NumEntries--) 170 { 171 RtlFreeUnicodeString(Hist->Entries++); 172 Hist->Position += (Hist->Position == 0); 173 } 174 175 Hist->MaxEntries = NumCommands; 176 RtlCopyMemory(NewEntryList, Hist->Entries, 177 Hist->NumEntries * sizeof(UNICODE_STRING)); 178 Hist->Entries = NewEntryList; 179 ConsoleFreeHeap(OldEntryList); 180 181 return STATUS_SUCCESS; 182 } 183 184 VOID 185 HistoryAddEntry(PCONSRV_CONSOLE Console, 186 PUNICODE_STRING ExeName, 187 PUNICODE_STRING Entry) 188 { 189 // UNICODE_STRING NewEntry; 190 PHISTORY_BUFFER Hist = HistoryCurrentBuffer(Console, ExeName); 191 192 if (!Hist) return; 193 194 // NewEntry.Length = NewEntry.MaximumLength = Console->LineSize * sizeof(WCHAR); 195 // NewEntry.Buffer = Console->LineBuffer; 196 197 /* Don't add blank or duplicate entries */ 198 if (Entry->Length == 0 || Hist->MaxEntries == 0 || 199 (Hist->NumEntries > 0 && 200 RtlEqualUnicodeString(&Hist->Entries[Hist->NumEntries - 1], Entry, FALSE))) 201 { 202 return; 203 } 204 205 if (Console->HistoryNoDup) 206 { 207 INT i; 208 209 /* Check if this line has been entered before */ 210 for (i = Hist->NumEntries - 1; i >= 0; i--) 211 { 212 if (RtlEqualUnicodeString(&Hist->Entries[i], Entry, FALSE)) 213 { 214 UNICODE_STRING NewEntry; 215 216 /* Just rotate the list to bring this entry to the end */ 217 NewEntry = Hist->Entries[i]; 218 RtlMoveMemory(&Hist->Entries[i], &Hist->Entries[i + 1], 219 (Hist->NumEntries - (i + 1)) * sizeof(UNICODE_STRING)); 220 Hist->Entries[Hist->NumEntries - 1] = NewEntry; 221 Hist->Position = Hist->NumEntries - 1; 222 return; 223 } 224 } 225 } 226 227 if (Hist->NumEntries == Hist->MaxEntries) 228 { 229 /* List is full, remove oldest entry */ 230 RtlFreeUnicodeString(&Hist->Entries[0]); 231 RtlMoveMemory(&Hist->Entries[0], &Hist->Entries[1], 232 --Hist->NumEntries * sizeof(UNICODE_STRING)); 233 } 234 235 if (NT_SUCCESS(RtlDuplicateUnicodeString(0, Entry, &Hist->Entries[Hist->NumEntries]))) 236 Hist->NumEntries++; 237 Hist->Position = Hist->NumEntries - 1; 238 } 239 240 VOID 241 HistoryGetCurrentEntry(PCONSRV_CONSOLE Console, 242 PUNICODE_STRING ExeName, 243 PUNICODE_STRING Entry) 244 { 245 PHISTORY_BUFFER Hist = HistoryCurrentBuffer(Console, ExeName); 246 247 if (!Hist || Hist->NumEntries == 0) 248 Entry->Length = 0; 249 else 250 *Entry = Hist->Entries[Hist->Position]; 251 } 252 253 BOOL 254 HistoryRecallHistory(PCONSRV_CONSOLE Console, 255 PUNICODE_STRING ExeName, 256 INT Offset, 257 PUNICODE_STRING Entry) 258 { 259 PHISTORY_BUFFER Hist = HistoryCurrentBuffer(Console, ExeName); 260 ULONG Position = 0; 261 262 if (!Hist || Hist->NumEntries == 0) return FALSE; 263 264 Position = Hist->Position + Offset; 265 Position = min(max(Position, 0), Hist->NumEntries - 1); 266 Hist->Position = Position; 267 268 *Entry = Hist->Entries[Hist->Position]; 269 return TRUE; 270 } 271 272 BOOL 273 HistoryFindEntryByPrefix(PCONSRV_CONSOLE Console, 274 PUNICODE_STRING ExeName, 275 PUNICODE_STRING Prefix, 276 PUNICODE_STRING Entry) 277 { 278 INT HistPos; 279 280 /* Search for history entries starting with input. */ 281 PHISTORY_BUFFER Hist = HistoryCurrentBuffer(Console, ExeName); 282 if (!Hist || Hist->NumEntries == 0) return FALSE; 283 284 /* 285 * Like Up/F5, on first time start from current (usually last) entry, 286 * but on subsequent times start at previous entry. 287 */ 288 if (Console->LineUpPressed) 289 Hist->Position = (Hist->Position ? Hist->Position : Hist->NumEntries) - 1; 290 Console->LineUpPressed = TRUE; 291 292 // Entry.Length = Console->LinePos * sizeof(WCHAR); // == Pos * sizeof(WCHAR) 293 // Entry.Buffer = Console->LineBuffer; 294 295 /* 296 * Keep going backwards, even wrapping around to the end, 297 * until we get back to starting point. 298 */ 299 HistPos = Hist->Position; 300 do 301 { 302 if (RtlPrefixUnicodeString(Prefix, &Hist->Entries[HistPos], FALSE)) 303 { 304 Hist->Position = HistPos; 305 *Entry = Hist->Entries[HistPos]; 306 return TRUE; 307 } 308 if (--HistPos < 0) HistPos += Hist->NumEntries; 309 } while (HistPos != Hist->Position); 310 311 return FALSE; 312 } 313 314 PPOPUP_WINDOW 315 HistoryDisplayCurrentHistory(PCONSRV_CONSOLE Console, 316 PUNICODE_STRING ExeName) 317 { 318 PCONSOLE_SCREEN_BUFFER ActiveBuffer; 319 PPOPUP_WINDOW Popup; 320 321 SHORT xLeft, yTop; 322 SHORT Width, Height; 323 324 PHISTORY_BUFFER Hist = HistoryCurrentBuffer(Console, ExeName); 325 326 if (!Hist) return NULL; 327 if (Hist->NumEntries == 0) return NULL; 328 329 if (GetType(Console->ActiveBuffer) != TEXTMODE_BUFFER) return NULL; 330 ActiveBuffer = Console->ActiveBuffer; 331 332 Width = 40; 333 Height = 10; 334 335 /* Center the popup window on the screen */ 336 xLeft = ActiveBuffer->ViewOrigin.X + (ActiveBuffer->ViewSize.X - Width ) / 2; 337 yTop = ActiveBuffer->ViewOrigin.Y + (ActiveBuffer->ViewSize.Y - Height) / 2; 338 339 /* Create the popup */ 340 Popup = CreatePopupWindow(Console, ActiveBuffer, 341 xLeft, yTop, Width, Height); 342 if (Popup == NULL) return NULL; 343 344 Popup->PopupInputRoutine = NULL; 345 346 return Popup; 347 } 348 349 VOID 350 HistoryDeleteCurrentBuffer(PCONSRV_CONSOLE Console, 351 PUNICODE_STRING ExeName) 352 { 353 HistoryDeleteBuffer(HistoryCurrentBuffer(Console, ExeName)); 354 } 355 356 VOID 357 HistoryDeleteBuffers(PCONSRV_CONSOLE Console) 358 { 359 PLIST_ENTRY CurrentEntry; 360 PHISTORY_BUFFER HistoryBuffer; 361 362 while (!IsListEmpty(&Console->HistoryBuffers)) 363 { 364 CurrentEntry = RemoveHeadList(&Console->HistoryBuffers); 365 HistoryBuffer = CONTAINING_RECORD(CurrentEntry, HISTORY_BUFFER, ListEntry); 366 HistoryDeleteBuffer(HistoryBuffer); 367 } 368 } 369 370 VOID 371 HistoryReshapeAllBuffers( 372 IN PCONSRV_CONSOLE Console, 373 IN ULONG HistoryBufferSize, 374 IN ULONG MaxNumberOfHistoryBuffers, 375 IN BOOLEAN HistoryNoDup) 376 { 377 PLIST_ENTRY Entry; 378 PHISTORY_BUFFER Hist; 379 NTSTATUS Status; 380 381 // ASSERT(Console->NumberOfHistoryBuffers <= Console->MaxNumberOfHistoryBuffers); 382 if (MaxNumberOfHistoryBuffers < Console->NumberOfHistoryBuffers) 383 { 384 /* 385 * Trim the history buffers list and reduce it up to MaxNumberOfHistoryBuffers. 386 * We loop the history buffers list backwards so as to trim the oldest 387 * buffers first. 388 */ 389 while (!IsListEmpty(&Console->HistoryBuffers) && 390 (Console->NumberOfHistoryBuffers > MaxNumberOfHistoryBuffers)) 391 { 392 Entry = RemoveTailList(&Console->HistoryBuffers); 393 Hist = CONTAINING_RECORD(Entry, HISTORY_BUFFER, ListEntry); 394 HistoryDeleteBuffer(Hist); 395 Console->NumberOfHistoryBuffers--; 396 } 397 ASSERT(Console->NumberOfHistoryBuffers == MaxNumberOfHistoryBuffers); 398 ASSERT(((Console->NumberOfHistoryBuffers == 0) && IsListEmpty(&Console->HistoryBuffers)) || 399 ((Console->NumberOfHistoryBuffers > 0) && !IsListEmpty(&Console->HistoryBuffers))); 400 } 401 Console->MaxNumberOfHistoryBuffers = MaxNumberOfHistoryBuffers; 402 403 /* Resize each history buffer */ 404 for (Entry = Console->HistoryBuffers.Flink; 405 Entry != &Console->HistoryBuffers; 406 Entry = Entry->Flink) 407 { 408 Hist = CONTAINING_RECORD(Entry, HISTORY_BUFFER, ListEntry); 409 Status = HistoryResizeBuffer(Hist, HistoryBufferSize); 410 if (!NT_SUCCESS(Status)) 411 { 412 DPRINT1("HistoryResizeBuffer(0x%p, %lu) failed, Status 0x%08lx\n", 413 Hist, HistoryBufferSize, Status); 414 } 415 } 416 Console->HistoryBufferSize = HistoryBufferSize; 417 418 /* No duplicates */ 419 Console->HistoryNoDup = !!HistoryNoDup; 420 } 421 422 423 /* PUBLIC SERVER APIS *********************************************************/ 424 425 /* API_NUMBER: ConsolepGetCommandHistory */ 426 CON_API(SrvGetConsoleCommandHistory, 427 CONSOLE_GETCOMMANDHISTORY, GetCommandHistoryRequest) 428 { 429 NTSTATUS Status = STATUS_SUCCESS; 430 ULONG BytesWritten = 0; 431 PHISTORY_BUFFER Hist; 432 433 if ( !CsrValidateMessageBuffer(ApiMessage, 434 (PVOID*)&GetCommandHistoryRequest->History, 435 GetCommandHistoryRequest->HistoryLength, 436 sizeof(BYTE)) || 437 !CsrValidateMessageBuffer(ApiMessage, 438 (PVOID*)&GetCommandHistoryRequest->ExeName, 439 GetCommandHistoryRequest->ExeLength, 440 sizeof(BYTE)) ) 441 { 442 return STATUS_INVALID_PARAMETER; 443 } 444 445 Hist = HistoryFindBuffer(Console, 446 GetCommandHistoryRequest->ExeName, 447 GetCommandHistoryRequest->ExeLength, 448 GetCommandHistoryRequest->Unicode2); 449 if (Hist) 450 { 451 ULONG i; 452 453 LPSTR TargetBufferA; 454 LPWSTR TargetBufferW; 455 ULONG BufferSize = GetCommandHistoryRequest->HistoryLength; 456 457 ULONG Offset = 0; 458 ULONG SourceLength; 459 460 if (GetCommandHistoryRequest->Unicode) 461 { 462 TargetBufferW = GetCommandHistoryRequest->History; 463 BufferSize /= sizeof(WCHAR); 464 } 465 else 466 { 467 TargetBufferA = GetCommandHistoryRequest->History; 468 } 469 470 for (i = 0; i < Hist->NumEntries; i++) 471 { 472 SourceLength = Hist->Entries[i].Length / sizeof(WCHAR); 473 if (Offset + SourceLength + 1 > BufferSize) 474 { 475 Status = STATUS_BUFFER_OVERFLOW; 476 break; 477 } 478 479 if (GetCommandHistoryRequest->Unicode) 480 { 481 RtlCopyMemory(&TargetBufferW[Offset], Hist->Entries[i].Buffer, SourceLength * sizeof(WCHAR)); 482 Offset += SourceLength; 483 TargetBufferW[Offset++] = L'\0'; 484 } 485 else 486 { 487 ConvertInputUnicodeToAnsi(Console, 488 Hist->Entries[i].Buffer, SourceLength * sizeof(WCHAR), 489 &TargetBufferA[Offset], SourceLength); 490 Offset += SourceLength; 491 TargetBufferA[Offset++] = '\0'; 492 } 493 } 494 495 if (GetCommandHistoryRequest->Unicode) 496 BytesWritten = Offset * sizeof(WCHAR); 497 else 498 BytesWritten = Offset; 499 } 500 501 // GetCommandHistoryRequest->HistoryLength = TargetBuffer - (PBYTE)GetCommandHistoryRequest->History; 502 GetCommandHistoryRequest->HistoryLength = BytesWritten; 503 504 return Status; 505 } 506 507 /* API_NUMBER: ConsolepGetCommandHistoryLength */ 508 CON_API(SrvGetConsoleCommandHistoryLength, 509 CONSOLE_GETCOMMANDHISTORYLENGTH, GetCommandHistoryLengthRequest) 510 { 511 PHISTORY_BUFFER Hist; 512 ULONG Length = 0; 513 514 if (!CsrValidateMessageBuffer(ApiMessage, 515 (PVOID*)&GetCommandHistoryLengthRequest->ExeName, 516 GetCommandHistoryLengthRequest->ExeLength, 517 sizeof(BYTE))) 518 { 519 return STATUS_INVALID_PARAMETER; 520 } 521 522 Hist = HistoryFindBuffer(Console, 523 GetCommandHistoryLengthRequest->ExeName, 524 GetCommandHistoryLengthRequest->ExeLength, 525 GetCommandHistoryLengthRequest->Unicode2); 526 if (Hist) 527 { 528 ULONG i; 529 for (i = 0; i < Hist->NumEntries; i++) 530 Length += Hist->Entries[i].Length + sizeof(WCHAR); // Each entry is returned NULL-terminated 531 } 532 /* 533 * Quick and dirty way of getting the number of bytes of the 534 * corresponding ANSI string from the one in UNICODE. 535 */ 536 if (!GetCommandHistoryLengthRequest->Unicode) 537 Length /= sizeof(WCHAR); 538 539 GetCommandHistoryLengthRequest->HistoryLength = Length; 540 541 return STATUS_SUCCESS; 542 } 543 544 /* API_NUMBER: ConsolepExpungeCommandHistory */ 545 CON_API(SrvExpungeConsoleCommandHistory, 546 CONSOLE_EXPUNGECOMMANDHISTORY, ExpungeCommandHistoryRequest) 547 { 548 PHISTORY_BUFFER Hist; 549 550 if (!CsrValidateMessageBuffer(ApiMessage, 551 (PVOID*)&ExpungeCommandHistoryRequest->ExeName, 552 ExpungeCommandHistoryRequest->ExeLength, 553 sizeof(BYTE))) 554 { 555 return STATUS_INVALID_PARAMETER; 556 } 557 558 Hist = HistoryFindBuffer(Console, 559 ExpungeCommandHistoryRequest->ExeName, 560 ExpungeCommandHistoryRequest->ExeLength, 561 ExpungeCommandHistoryRequest->Unicode2); 562 HistoryDeleteBuffer(Hist); 563 564 return STATUS_SUCCESS; 565 } 566 567 /* API_NUMBER: ConsolepSetNumberOfCommands */ 568 CON_API(SrvSetConsoleNumberOfCommands, 569 CONSOLE_SETHISTORYNUMBERCOMMANDS, SetHistoryNumberCommandsRequest) 570 { 571 NTSTATUS Status = STATUS_SUCCESS; 572 PHISTORY_BUFFER Hist; 573 574 if (!CsrValidateMessageBuffer(ApiMessage, 575 (PVOID*)&SetHistoryNumberCommandsRequest->ExeName, 576 SetHistoryNumberCommandsRequest->ExeLength, 577 sizeof(BYTE))) 578 { 579 return STATUS_INVALID_PARAMETER; 580 } 581 582 Hist = HistoryFindBuffer(Console, 583 SetHistoryNumberCommandsRequest->ExeName, 584 SetHistoryNumberCommandsRequest->ExeLength, 585 SetHistoryNumberCommandsRequest->Unicode2); 586 if (Hist) 587 { 588 Status = HistoryResizeBuffer(Hist, SetHistoryNumberCommandsRequest->NumCommands); 589 } 590 591 return Status; 592 } 593 594 /* API_NUMBER: ConsolepGetHistory */ 595 CON_API_NOCONSOLE(SrvGetConsoleHistory, 596 CONSOLE_GETSETHISTORYINFO, HistoryInfoRequest) 597 { 598 #if (_WIN32_WINNT >= _WIN32_WINNT_VISTA) 599 NTSTATUS Status; 600 PCONSRV_CONSOLE Console; 601 602 Status = ConSrvGetConsole(ProcessData, 603 &Console, TRUE); 604 if (!NT_SUCCESS(Status)) 605 return Status; 606 607 HistoryInfoRequest->HistoryBufferSize = Console->HistoryBufferSize; 608 HistoryInfoRequest->NumberOfHistoryBuffers = Console->MaxNumberOfHistoryBuffers; 609 HistoryInfoRequest->dwFlags = (Console->HistoryNoDup ? HISTORY_NO_DUP_FLAG : 0); 610 611 ConSrvReleaseConsole(Console, TRUE); 612 return Status; 613 #else 614 DPRINT1("%s not yet implemented\n", __FUNCTION__); 615 return STATUS_NOT_IMPLEMENTED; 616 #endif 617 } 618 619 /* API_NUMBER: ConsolepSetHistory */ 620 CON_API_NOCONSOLE(SrvSetConsoleHistory, 621 CONSOLE_GETSETHISTORYINFO, HistoryInfoRequest) 622 { 623 #if (_WIN32_WINNT >= _WIN32_WINNT_VISTA) 624 NTSTATUS Status; 625 PCONSRV_CONSOLE Console; 626 627 Status = ConSrvGetConsole(ProcessData, 628 &Console, TRUE); 629 if (!NT_SUCCESS(Status)) 630 return Status; 631 632 HistoryReshapeAllBuffers(Console, 633 HistoryInfoRequest->HistoryBufferSize, 634 HistoryInfoRequest->NumberOfHistoryBuffers, 635 !!(HistoryInfoRequest->dwFlags & HISTORY_NO_DUP_FLAG)); 636 637 ConSrvReleaseConsole(Console, TRUE); 638 return STATUS_SUCCESS; 639 #else 640 DPRINT1("%s not yet implemented\n", __FUNCTION__); 641 return STATUS_NOT_IMPLEMENTED; 642 #endif 643 } 644 645 /* API_NUMBER: ConsolepSetCommandHistoryMode */ 646 CON_API(SrvSetConsoleCommandHistoryMode, 647 CONSOLE_SETHISTORYMODE, SetHistoryModeRequest) 648 { 649 DPRINT("SrvSetConsoleCommandHistoryMode(Mode = %d) is not yet implemented\n", 650 SetHistoryModeRequest->Mode); 651 652 Console->InsertMode = !!(SetHistoryModeRequest->Mode & CONSOLE_OVERSTRIKE); 653 return STATUS_SUCCESS; 654 } 655 656 /* EOF */ 657