Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5180 lines
176 KiB

  1. /*++
  2. Copyright (c) 1985 - 1999, Microsoft Corporation
  3. Module Name:
  4. cmdline.c
  5. Abstract:
  6. This file implements command line editing and aliasing.
  7. Author:
  8. Therese Stowell (thereses) 22-Mar-1991
  9. Revision History:
  10. Notes:
  11. The input model for the command line editing popups is complex.
  12. Here is the relevant pseudocode:
  13. CookedReadWaitRoutine
  14. if (CookedRead->Popup)
  15. Status = (*CookedRead->Popup->PopupInputRoutine)();
  16. if (Status == CONSOLE_STATUS_READ_COMPLETE)
  17. return STATUS_SUCCESS;
  18. return Status;
  19. CookedRead
  20. if (Command Line Editing Key)
  21. ProcessCommandLine
  22. else
  23. process regular key
  24. ProcessCommandLine
  25. if F7
  26. return CommandLinePopup
  27. CommandLinePopup
  28. draw popup
  29. return ProcessCommandListInput
  30. ProcessCommandListInput
  31. while (TRUE)
  32. GetChar
  33. if (wait)
  34. return wait
  35. switch (char)
  36. .
  37. .
  38. .
  39. --*/
  40. #include "precomp.h"
  41. #pragma hdrstop
  42. #define COPY_TO_CHAR_PROMPT_LENGTH 26
  43. #define COPY_FROM_CHAR_PROMPT_LENGTH 28
  44. #define COMMAND_NUMBER_PROMPT_LENGTH 22
  45. #define COMMAND_NUMBER_LENGTH 5
  46. #define MINIMUM_COMMAND_PROMPT_SIZE COMMAND_NUMBER_LENGTH
  47. #if defined(FE_SB)
  48. #define CHAR_COUNT(cch) cch
  49. #else
  50. #define CHAR_COUNT(cch) ((cch)/sizeof(WCHAR))
  51. #endif
  52. #define ALT_PRESSED (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)
  53. #define CTRL_PRESSED (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)
  54. #define CTRL_BUT_NOT_ALT(n) \
  55. (((n) & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) && \
  56. !((n) & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)))
  57. //
  58. // Extended Edit Key
  59. //
  60. ExtKeyDefTable gaKeyDef;
  61. CONST ExtKeyDefTable gaDefaultKeyDef = {
  62. { // A
  63. 0, VK_HOME, 0, // Ctrl
  64. LEFT_CTRL_PRESSED, VK_HOME, 0, // Alt
  65. 0, 0, 0, // Ctrl+Alt
  66. },
  67. { // B
  68. 0, VK_LEFT, 0, // Ctrl
  69. LEFT_CTRL_PRESSED, VK_LEFT, 0, // Alt
  70. },
  71. { // C
  72. 0,
  73. },
  74. { // D
  75. 0, VK_DELETE, 0, // Ctrl
  76. LEFT_CTRL_PRESSED, VK_DELETE, 0, // Alt
  77. 0, 0, 0, // Ctrl+Alt
  78. },
  79. { // E
  80. 0, VK_END, 0, // Ctrl
  81. LEFT_CTRL_PRESSED, VK_END, 0, // Alt
  82. 0, 0, 0, // Ctrl+Alt
  83. },
  84. { // F
  85. 0, VK_RIGHT, 0, // Ctrl
  86. LEFT_CTRL_PRESSED, VK_RIGHT, 0, // Alt
  87. 0, 0, 0, // Ctrl+Alt
  88. },
  89. { // G
  90. 0,
  91. },
  92. { // H
  93. 0,
  94. },
  95. { // I
  96. 0,
  97. },
  98. { // J
  99. 0,
  100. },
  101. { // K
  102. LEFT_CTRL_PRESSED, VK_END, 0, // Ctrl
  103. },
  104. { // L
  105. 0,
  106. },
  107. { // M
  108. 0,
  109. },
  110. { // N
  111. 0, VK_DOWN, 0, // Ctrl
  112. },
  113. { // O
  114. 0,
  115. },
  116. { // P
  117. 0, VK_UP, 0, // Ctrl
  118. },
  119. { // Q
  120. 0,
  121. },
  122. { // R
  123. 0, VK_F8, 0, // Ctrl
  124. },
  125. { // S
  126. 0, VK_PAUSE, 0, // Ctrl
  127. },
  128. { // T
  129. LEFT_CTRL_PRESSED, VK_DELETE, 0, // Ctrl
  130. },
  131. { // U
  132. 0, VK_ESCAPE, 0, // Ctrl
  133. },
  134. { // V
  135. 0,
  136. },
  137. { // W
  138. LEFT_CTRL_PRESSED, VK_BACK, EXTKEY_ERASE_PREV_WORD, // Ctrl
  139. },
  140. { // X
  141. 0,
  142. },
  143. { // Y
  144. 0,
  145. },
  146. { // Z
  147. 0,
  148. },
  149. };
  150. //
  151. // InitExtendedEditKeys
  152. //
  153. // Initialize the extended edit key table.
  154. // If pKeyDefbuf is NULL, the internal default table is used.
  155. // Otherwise, lpbyte should point to a valid ExtKeyDefBuf.
  156. //
  157. VOID InitExtendedEditKeys(CONST ExtKeyDefBuf* pKeyDefBuf)
  158. {
  159. CONST BYTE* lpbyte;
  160. int i;
  161. DWORD dwCheckSum;
  162. //
  163. // Sanity check
  164. // If pKeyDefBuf is NULL, give it the default value.
  165. // If the version is not supported, just use the default and bail.
  166. //
  167. if (pKeyDefBuf == NULL || pKeyDefBuf->dwVersion != 0) {
  168. #if DBG
  169. if (pKeyDefBuf != NULL) {
  170. DbgPrint("InitExtendedEditKeys: Unsupported version number(%d)\n", pKeyDefBuf->dwVersion);
  171. }
  172. #endif
  173. retry_clean:
  174. memcpy(gaKeyDef, gaDefaultKeyDef, sizeof gaKeyDef);
  175. return;
  176. }
  177. //
  178. // Calculate check sum
  179. //
  180. dwCheckSum = 0;
  181. for (lpbyte = (CONST BYTE*)pKeyDefBuf, i = FIELD_OFFSET(ExtKeyDefBuf, table); i < sizeof *pKeyDefBuf; ++i) {
  182. dwCheckSum += lpbyte[i];
  183. }
  184. if (dwCheckSum != pKeyDefBuf->dwCheckSum) {
  185. #if DBG
  186. DbgPrint("InitExtendedEditKeys: Checksum(%d) does not match.\n", pKeyDefBuf->dwCheckSum);
  187. #endif
  188. goto retry_clean;
  189. }
  190. //
  191. // Copy the entity
  192. //
  193. memcpy(gaKeyDef, pKeyDefBuf->table, sizeof gaKeyDef);
  194. }
  195. CONST ExtKeySubst* ParseEditKeyInfo(IN OUT PKEY_EVENT_RECORD pKeyEvent)
  196. {
  197. CONST ExtKeyDef* pKeyDef;
  198. CONST ExtKeySubst* pKeySubst;
  199. //
  200. // If not extended mode, or Control key or Alt key is not pressed,
  201. // or virtual keycode is out of range, just bail.
  202. //
  203. if (!gExtendedEditKey ||
  204. (pKeyEvent->dwControlKeyState & (CTRL_PRESSED | ALT_PRESSED)) == 0 ||
  205. pKeyEvent->wVirtualKeyCode < 'A' || pKeyEvent->wVirtualKeyCode > 'Z') {
  206. return NULL;
  207. }
  208. //
  209. // Get the corresponding KeyDef.
  210. //
  211. pKeyDef = &gaKeyDef[pKeyEvent->wVirtualKeyCode - 'A'];
  212. //
  213. // Get the KeySubst based on the modifier status.
  214. //
  215. if (pKeyEvent->dwControlKeyState & ALT_PRESSED) {
  216. if (pKeyEvent->dwControlKeyState & CTRL_PRESSED) {
  217. pKeySubst = &pKeyDef->keys[2];
  218. } else {
  219. pKeySubst = &pKeyDef->keys[1];
  220. }
  221. } else {
  222. UserAssert(pKeyEvent->dwControlKeyState & CTRL_PRESSED);
  223. pKeySubst = &pKeyDef->keys[0];
  224. }
  225. UserAssert(pKeySubst);
  226. //
  227. // If the conbination is not defined, just bail.
  228. //
  229. if (pKeySubst->wVirKey == 0) {
  230. return NULL;
  231. }
  232. //
  233. // Substitute the input with ext key.
  234. //
  235. pKeyEvent->dwControlKeyState = pKeySubst->wMod;
  236. pKeyEvent->wVirtualKeyCode = pKeySubst->wVirKey;
  237. pKeyEvent->uChar.UnicodeChar = pKeySubst->wUnicodeChar;
  238. return pKeySubst;
  239. }
  240. //
  241. // IsPauseKey
  242. // returns TRUE if pKeyEvent is pause.
  243. // The default key is Ctrl-S if extended edit keys are not specified.
  244. //
  245. BOOL IsPauseKey(IN PKEY_EVENT_RECORD pKeyEvent)
  246. {
  247. if (gExtendedEditKey) {
  248. KEY_EVENT_RECORD KeyEvent = *pKeyEvent;
  249. CONST ExtKeySubst* pKeySubst = ParseEditKeyInfo(&KeyEvent);
  250. return pKeySubst != NULL && pKeySubst->wVirKey == VK_PAUSE;
  251. }
  252. return pKeyEvent->wVirtualKeyCode == L'S' && CTRL_BUT_NOT_ALT(pKeyEvent->dwControlKeyState);
  253. }
  254. //
  255. // Word delimiters
  256. //
  257. WCHAR gaWordDelimChars[WORD_DELIM_MAX];
  258. CONST WCHAR gaWordDelimCharsDefault[WORD_DELIM_MAX] = L"\\" L"+!:=/.<>;|&";
  259. BOOL IsWordDelim(WCHAR wch)
  260. {
  261. int i;
  262. //
  263. // Before it reaches here, L' ' case should have beeen already detected,
  264. // and gaWordDelimChars is specified.
  265. //
  266. UserAssert(wch != L' ' && gaWordDelimChars[0]);
  267. for (i = 0; gaWordDelimChars[i] && i < WORD_DELIM_MAX; ++i) {
  268. if (wch == gaWordDelimChars[i]) {
  269. return TRUE;
  270. }
  271. }
  272. return FALSE;
  273. }
  274. PEXE_ALIAS_LIST
  275. AddExeAliasList(
  276. IN PCONSOLE_INFORMATION Console,
  277. IN LPVOID ExeName,
  278. IN USHORT ExeLength, // in bytes
  279. IN BOOLEAN UnicodeExe
  280. )
  281. {
  282. PEXE_ALIAS_LIST AliasList;
  283. AliasList = ConsoleHeapAlloc(ALIAS_TAG, sizeof(EXE_ALIAS_LIST));
  284. if (AliasList == NULL) {
  285. return NULL;
  286. }
  287. if (UnicodeExe) {
  288. AliasList->ExeName = ConsoleHeapAlloc(ALIAS_TAG, ExeLength);
  289. if (AliasList->ExeName == NULL) {
  290. ConsoleHeapFree(AliasList);
  291. return NULL;
  292. }
  293. RtlCopyMemory(AliasList->ExeName,ExeName,ExeLength);
  294. AliasList->ExeLength = ExeLength;
  295. } else {
  296. AliasList->ExeName = ConsoleHeapAlloc(ALIAS_TAG, ExeLength*sizeof(WCHAR));
  297. if (AliasList->ExeName == NULL) {
  298. ConsoleHeapFree(AliasList);
  299. return NULL;
  300. }
  301. AliasList->ExeLength = (USHORT)ConvertInputToUnicode(Console->CP,
  302. ExeName,
  303. ExeLength,
  304. AliasList->ExeName,
  305. ExeLength);
  306. AliasList->ExeLength *= 2;
  307. }
  308. InitializeListHead(&AliasList->AliasList);
  309. InsertHeadList(&Console->ExeAliasList,&AliasList->ListLink);
  310. return AliasList;
  311. }
  312. int
  313. MyStringCompareW(
  314. IN LPWSTR Str1,
  315. IN LPWSTR Str2,
  316. IN USHORT Length, // in bytes
  317. IN BOOLEAN bCaseInsensitive
  318. )
  319. {
  320. UNICODE_STRING String1;
  321. UNICODE_STRING String2;
  322. String1.Length = Length;
  323. String1.MaximumLength = Length;
  324. String1.Buffer = Str1;
  325. String2.Length = Length;
  326. String2.MaximumLength = Length;
  327. String2.Buffer = Str2;
  328. return RtlCompareUnicodeString(&String1,
  329. &String2,
  330. bCaseInsensitive);
  331. }
  332. #define my_wcsncmpi(p1, p2, n) MyStringCompareW(p1, p2, n, TRUE)
  333. #define my_wcsncmp(p1, p2, n) MyStringCompareW(p1, p2, n, FALSE)
  334. PEXE_ALIAS_LIST
  335. FindExe(
  336. IN PCONSOLE_INFORMATION Console,
  337. IN LPVOID ExeName,
  338. IN USHORT ExeLength, // in bytes
  339. IN BOOLEAN UnicodeExe
  340. )
  341. /*++
  342. This routine searches for the specified exe alias list. It returns
  343. a pointer to the exe list if found, NULL if not found.
  344. --*/
  345. {
  346. PEXE_ALIAS_LIST AliasList;
  347. PLIST_ENTRY ListHead, ListNext;
  348. LPWSTR UnicodeExeName;
  349. if (UnicodeExe) {
  350. UnicodeExeName = ExeName;
  351. } else {
  352. UnicodeExeName = ConsoleHeapAlloc(TMP_TAG, ExeLength * sizeof(WCHAR));
  353. if (UnicodeExeName == NULL)
  354. return NULL;
  355. ExeLength = (USHORT)ConvertInputToUnicode(Console->CP,
  356. ExeName,
  357. ExeLength,
  358. UnicodeExeName,
  359. ExeLength);
  360. ExeLength *= 2;
  361. }
  362. ListHead = &Console->ExeAliasList;
  363. ListNext = ListHead->Flink;
  364. while (ListNext != ListHead) {
  365. AliasList = CONTAINING_RECORD( ListNext, EXE_ALIAS_LIST, ListLink );
  366. if (AliasList->ExeLength == ExeLength &&
  367. !my_wcsncmpi(AliasList->ExeName,UnicodeExeName,ExeLength)) {
  368. if (!UnicodeExe) {
  369. ConsoleHeapFree(UnicodeExeName);
  370. }
  371. return AliasList;
  372. }
  373. ListNext = ListNext->Flink;
  374. }
  375. if (!UnicodeExe) {
  376. ConsoleHeapFree(UnicodeExeName);
  377. }
  378. return NULL;
  379. }
  380. PALIAS
  381. FindAlias(
  382. IN PEXE_ALIAS_LIST AliasList,
  383. IN LPWSTR AliasName,
  384. IN USHORT AliasLength // in bytes
  385. )
  386. /*++
  387. This routine searches for the specified alias. If it finds one,
  388. it moves it to the head of the list and returns a pointer to the
  389. alias. Otherwise it returns NULL.
  390. --*/
  391. {
  392. PALIAS Alias;
  393. PLIST_ENTRY ListHead, ListNext;
  394. ListHead = &AliasList->AliasList;
  395. ListNext = ListHead->Flink;
  396. while (ListNext != ListHead) {
  397. Alias = CONTAINING_RECORD( ListNext, ALIAS, ListLink );
  398. if (Alias->SourceLength == AliasLength &&
  399. !my_wcsncmpi(Alias->Source,AliasName,AliasLength)) {
  400. if (ListNext != ListHead->Flink) {
  401. RemoveEntryList(ListNext);
  402. InsertHeadList(ListHead,ListNext);
  403. }
  404. return Alias;
  405. }
  406. ListNext = ListNext->Flink;
  407. }
  408. return NULL;
  409. }
  410. NTSTATUS
  411. AddAlias(
  412. IN PEXE_ALIAS_LIST ExeAliasList,
  413. IN LPWSTR Source,
  414. IN USHORT SourceLength, // in bytes
  415. IN LPWSTR Target,
  416. IN USHORT TargetLength // in bytes
  417. )
  418. /*++
  419. This routine creates an alias and inserts it into the exe alias list.
  420. --*/
  421. {
  422. PALIAS Alias;
  423. Alias = ConsoleHeapAlloc(ALIAS_TAG, sizeof(ALIAS));
  424. if (Alias == NULL) {
  425. return STATUS_NO_MEMORY;
  426. }
  427. Alias->Source = ConsoleHeapAlloc(ALIAS_TAG, SourceLength);
  428. if (Alias->Source == NULL) {
  429. ConsoleHeapFree(Alias);
  430. return STATUS_NO_MEMORY;
  431. }
  432. Alias->Target = ConsoleHeapAlloc(ALIAS_TAG, TargetLength);
  433. if (Alias->Target == NULL) {
  434. ConsoleHeapFree(Alias->Source);
  435. ConsoleHeapFree(Alias);
  436. return STATUS_NO_MEMORY;
  437. }
  438. Alias->SourceLength = SourceLength;
  439. Alias->TargetLength = TargetLength;
  440. RtlCopyMemory(Alias->Source,Source,SourceLength);
  441. RtlCopyMemory(Alias->Target,Target,TargetLength);
  442. InsertHeadList(&ExeAliasList->AliasList,&Alias->ListLink);
  443. return STATUS_SUCCESS;
  444. }
  445. NTSTATUS
  446. ReplaceAlias(
  447. IN PALIAS Alias,
  448. IN LPWSTR Target,
  449. IN USHORT TargetLength // in bytes
  450. )
  451. /*++
  452. This routine replaces an existing target with a new target.
  453. --*/
  454. {
  455. LPWSTR NewTarget;
  456. NewTarget = ConsoleHeapAlloc(ALIAS_TAG, TargetLength);
  457. if (NewTarget == NULL) {
  458. return STATUS_NO_MEMORY;
  459. }
  460. ConsoleHeapFree(Alias->Target);
  461. Alias->Target = NewTarget;
  462. Alias->TargetLength = TargetLength;
  463. RtlCopyMemory(Alias->Target,Target,TargetLength);
  464. return STATUS_SUCCESS;
  465. }
  466. NTSTATUS
  467. RemoveAlias(
  468. IN PALIAS Alias
  469. )
  470. /*++
  471. This routine removes an alias.
  472. --*/
  473. {
  474. RemoveEntryList(&Alias->ListLink);
  475. ConsoleHeapFree(Alias->Source);
  476. ConsoleHeapFree(Alias->Target);
  477. ConsoleHeapFree(Alias);
  478. return STATUS_SUCCESS;
  479. }
  480. VOID
  481. FreeAliasList(
  482. IN PEXE_ALIAS_LIST ExeAliasList
  483. )
  484. {
  485. PLIST_ENTRY ListHead, ListNext;
  486. PALIAS Alias;
  487. ListHead = &ExeAliasList->AliasList;
  488. ListNext = ListHead->Flink;
  489. while (ListNext != ListHead) {
  490. Alias = CONTAINING_RECORD( ListNext, ALIAS, ListLink );
  491. ListNext = ListNext->Flink;
  492. RemoveAlias(Alias);
  493. }
  494. RemoveEntryList(&ExeAliasList->ListLink);
  495. ConsoleHeapFree(ExeAliasList->ExeName);
  496. ConsoleHeapFree(ExeAliasList);
  497. }
  498. VOID
  499. FreeAliasBuffers(
  500. IN PCONSOLE_INFORMATION Console
  501. )
  502. {
  503. PEXE_ALIAS_LIST AliasList;
  504. PLIST_ENTRY ListHead, ListNext;
  505. ListHead = &Console->ExeAliasList;
  506. ListNext = ListHead->Flink;
  507. while (ListNext != ListHead) {
  508. AliasList = CONTAINING_RECORD( ListNext, EXE_ALIAS_LIST, ListLink );
  509. ListNext = ListNext->Flink;
  510. FreeAliasList(AliasList);
  511. }
  512. }
  513. ULONG
  514. SrvAddConsoleAlias(
  515. IN OUT PCSR_API_MSG m,
  516. IN OUT PCSR_REPLY_STATUS ReplyStatus
  517. )
  518. /*++
  519. Routine Description:
  520. This routine adds a command line alias to the global set.
  521. Arguments:
  522. m - message containing api parameters
  523. ReplyStatus - Indicates whether to reply to the dll port.
  524. Return Value:
  525. --*/
  526. {
  527. PCONSOLE_ADDALIAS_MSG a = (PCONSOLE_ADDALIAS_MSG)&m->u.ApiMessageData;
  528. PALIAS Alias;
  529. PCONSOLE_INFORMATION Console;
  530. PEXE_ALIAS_LIST ExeAliasList;
  531. NTSTATUS Status;
  532. LPWSTR Source,Target;
  533. Status = ApiPreamble(a->ConsoleHandle,
  534. &Console
  535. );
  536. if (!NT_SUCCESS(Status)) {
  537. return Status;
  538. }
  539. if (!CsrValidateMessageBuffer(m, &a->Source, a->SourceLength, sizeof(BYTE)) ||
  540. !CsrValidateMessageBuffer(m, &a->Target,a->TargetLength, sizeof(BYTE)) ||
  541. !CsrValidateMessageBuffer(m, &a->Exe, a->ExeLength, sizeof(BYTE))) {
  542. UnlockConsole(Console);
  543. return STATUS_INVALID_PARAMETER;
  544. }
  545. if (a->Unicode) {
  546. Source = a->Source;
  547. Target = a->Target;
  548. } else {
  549. Source = ConsoleHeapAlloc(TMP_TAG, a->SourceLength * sizeof(WCHAR));
  550. if (Source == NULL) {
  551. UnlockConsole(Console);
  552. return STATUS_NO_MEMORY;
  553. }
  554. Target = ConsoleHeapAlloc(TMP_TAG, a->TargetLength * sizeof(WCHAR));
  555. if (Target == NULL) {
  556. ConsoleHeapFree(Source);
  557. UnlockConsole(Console);
  558. return STATUS_NO_MEMORY;
  559. }
  560. a->SourceLength = (USHORT)ConvertInputToUnicode(Console->CP,
  561. a->Source,
  562. a->SourceLength,
  563. Source,
  564. a->SourceLength);
  565. a->SourceLength *= 2;
  566. a->TargetLength = (USHORT)ConvertInputToUnicode(Console->CP,
  567. a->Target,
  568. a->TargetLength,
  569. Target,
  570. a->TargetLength);
  571. a->TargetLength *= 2;
  572. }
  573. //
  574. // find specified exe. if it's not there, add it if we're not
  575. // removing an alias.
  576. //
  577. ExeAliasList = FindExe(Console,a->Exe,a->ExeLength,a->UnicodeExe);
  578. if (ExeAliasList) {
  579. Alias = FindAlias(ExeAliasList,Source,a->SourceLength);
  580. if (a->TargetLength) {
  581. if (Alias) {
  582. Status = ReplaceAlias(Alias,
  583. Target,
  584. a->TargetLength);
  585. } else {
  586. Status = AddAlias(ExeAliasList,
  587. Source,
  588. a->SourceLength,
  589. Target,
  590. a->TargetLength);
  591. }
  592. } else {
  593. if (Alias) {
  594. Status = RemoveAlias(Alias);
  595. }
  596. }
  597. } else {
  598. if (a->TargetLength) {
  599. ExeAliasList = AddExeAliasList(Console,a->Exe,a->ExeLength,a->UnicodeExe);
  600. if (ExeAliasList) {
  601. Status = AddAlias(ExeAliasList,
  602. Source,
  603. a->SourceLength,
  604. Target,
  605. a->TargetLength);
  606. } else {
  607. Status = STATUS_NO_MEMORY;
  608. }
  609. }
  610. }
  611. UnlockConsole(Console);
  612. if (!a->Unicode) {
  613. ConsoleHeapFree(Source);
  614. ConsoleHeapFree(Target);
  615. }
  616. return Status;
  617. UNREFERENCED_PARAMETER(ReplyStatus);
  618. }
  619. ULONG
  620. SrvGetConsoleAlias(
  621. IN OUT PCSR_API_MSG m,
  622. IN OUT PCSR_REPLY_STATUS ReplyStatus
  623. )
  624. /*++
  625. Routine Description:
  626. This routine get a command line alias from the global set.
  627. Arguments:
  628. m - message containing api parameters
  629. ReplyStatus - Indicates whether to reply to the dll port.
  630. Return Value:
  631. --*/
  632. {
  633. NTSTATUS Status;
  634. PCONSOLE_GETALIAS_MSG a = (PCONSOLE_GETALIAS_MSG)&m->u.ApiMessageData;
  635. PALIAS Alias;
  636. PCONSOLE_INFORMATION Console;
  637. PEXE_ALIAS_LIST ExeAliasList;
  638. LPWSTR Source,Target;
  639. Status = ApiPreamble(a->ConsoleHandle,
  640. &Console
  641. );
  642. if (!NT_SUCCESS(Status)) {
  643. return Status;
  644. }
  645. if (!CsrValidateMessageBuffer(m, &a->Source, a->SourceLength, sizeof(BYTE)) ||
  646. !CsrValidateMessageBuffer(m, &a->Target, a->TargetLength, sizeof(BYTE)) ||
  647. !CsrValidateMessageBuffer(m, &a->Exe, a->ExeLength, sizeof(BYTE))) {
  648. UnlockConsole(Console);
  649. return STATUS_INVALID_PARAMETER;
  650. }
  651. if (a->Unicode) {
  652. Source = a->Source;
  653. Target = a->Target;
  654. } else {
  655. Source = ConsoleHeapAlloc(TMP_TAG, a->SourceLength * sizeof(WCHAR));
  656. if (Source == NULL) {
  657. UnlockConsole(Console);
  658. return STATUS_NO_MEMORY;
  659. }
  660. Target = ConsoleHeapAlloc(TMP_TAG, a->TargetLength * sizeof(WCHAR));
  661. if (Target == NULL) {
  662. ConsoleHeapFree(Source);
  663. UnlockConsole(Console);
  664. return STATUS_NO_MEMORY;
  665. }
  666. a->TargetLength = (USHORT)(a->TargetLength * sizeof(WCHAR));
  667. a->SourceLength = (USHORT)ConvertInputToUnicode(Console->CP,
  668. a->Source,
  669. a->SourceLength,
  670. Source,
  671. a->SourceLength);
  672. a->SourceLength *= 2;
  673. }
  674. ExeAliasList = FindExe(Console,a->Exe,a->ExeLength,a->UnicodeExe);
  675. if (ExeAliasList) {
  676. Alias = FindAlias(ExeAliasList,Source,a->SourceLength);
  677. if (Alias) {
  678. if (Alias->TargetLength + sizeof(WCHAR) > a->TargetLength) {
  679. Status = STATUS_BUFFER_TOO_SMALL;
  680. } else {
  681. a->TargetLength = Alias->TargetLength + sizeof(WCHAR);
  682. RtlCopyMemory(Target,Alias->Target,Alias->TargetLength);
  683. Target[Alias->TargetLength/sizeof(WCHAR)] = L'\0';
  684. }
  685. } else {
  686. Status = STATUS_UNSUCCESSFUL;
  687. }
  688. } else {
  689. Status = STATUS_UNSUCCESSFUL;
  690. }
  691. if (!a->Unicode) {
  692. if (NT_SUCCESS(Status)) {
  693. a->TargetLength = (USHORT)ConvertToOem(Console->CP,
  694. Target,
  695. a->TargetLength / sizeof(WCHAR),
  696. a->Target,
  697. CHAR_COUNT(a->TargetLength)
  698. );
  699. }
  700. ConsoleHeapFree(Source);
  701. ConsoleHeapFree(Target);
  702. }
  703. UnlockConsole(Console);
  704. return Status;
  705. UNREFERENCED_PARAMETER(ReplyStatus);
  706. }
  707. DWORD
  708. SrvGetConsoleAliasesLength(
  709. IN OUT PCSR_API_MSG m,
  710. IN OUT PCSR_REPLY_STATUS ReplyStatus
  711. )
  712. {
  713. PCONSOLE_GETALIASESLENGTH_MSG a = (PCONSOLE_GETALIASESLENGTH_MSG)&m->u.ApiMessageData;
  714. PCONSOLE_INFORMATION Console;
  715. PEXE_ALIAS_LIST ExeAliasList;
  716. PALIAS Alias;
  717. PLIST_ENTRY ListHead, ListNext;
  718. NTSTATUS Status;
  719. Status = ApiPreamble(a->ConsoleHandle,
  720. &Console
  721. );
  722. if (!NT_SUCCESS(Status)) {
  723. return Status;
  724. }
  725. if (!CsrValidateMessageBuffer(m, &a->Exe, a->ExeLength, sizeof(BYTE))) {
  726. UnlockConsole(Console);
  727. return STATUS_INVALID_PARAMETER;
  728. }
  729. a->AliasesLength = 0;
  730. ExeAliasList = FindExe(Console,a->Exe,a->ExeLength,a->UnicodeExe);
  731. if (ExeAliasList) {
  732. ListHead = &ExeAliasList->AliasList;
  733. ListNext = ListHead->Flink;
  734. while (ListNext != ListHead) {
  735. Alias = CONTAINING_RECORD( ListNext, ALIAS, ListLink );
  736. a->AliasesLength += Alias->SourceLength + Alias->TargetLength + (2*sizeof(WCHAR)); // +2 is for = and term null
  737. ListNext = ListNext->Flink;
  738. }
  739. }
  740. if (!a->Unicode) {
  741. a->AliasesLength /= sizeof(WCHAR);
  742. }
  743. UnlockConsole(Console);
  744. return STATUS_SUCCESS;
  745. UNREFERENCED_PARAMETER(ReplyStatus);
  746. }
  747. VOID
  748. ClearAliases(
  749. IN PCONSOLE_INFORMATION Console
  750. )
  751. {
  752. PEXE_ALIAS_LIST ExeAliasList;
  753. PLIST_ENTRY ListHead, ListNext;
  754. PALIAS Alias;
  755. ExeAliasList = FindExe(Console,
  756. L"cmd.exe",
  757. 14,
  758. TRUE);
  759. if (ExeAliasList == NULL) {
  760. return;
  761. }
  762. ListHead = &ExeAliasList->AliasList;
  763. ListNext = ListHead->Flink;
  764. while (ListNext != ListHead) {
  765. Alias = CONTAINING_RECORD( ListNext, ALIAS, ListLink );
  766. ListNext = ListNext->Flink;
  767. RemoveAlias(Alias);
  768. }
  769. }
  770. DWORD
  771. SrvGetConsoleAliases(
  772. IN OUT PCSR_API_MSG m,
  773. IN OUT PCSR_REPLY_STATUS ReplyStatus
  774. )
  775. {
  776. PCONSOLE_GETALIASES_MSG a = (PCONSOLE_GETALIASES_MSG)&m->u.ApiMessageData;
  777. PCONSOLE_INFORMATION Console;
  778. PEXE_ALIAS_LIST ExeAliasList;
  779. PALIAS Alias;
  780. PLIST_ENTRY ListHead, ListNext;
  781. DWORD AliasesBufferLength;
  782. LPWSTR AliasesBufferPtrW;
  783. LPSTR AliasesBufferPtrA;
  784. NTSTATUS Status;
  785. Status = ApiPreamble(a->ConsoleHandle,
  786. &Console
  787. );
  788. if (!NT_SUCCESS(Status)) {
  789. return Status;
  790. }
  791. if (!CsrValidateMessageBuffer(m, &a->AliasesBuffer, a->AliasesBufferLength, sizeof(BYTE)) ||
  792. !CsrValidateMessageBuffer(m, &a->Exe, a->ExeLength, sizeof(BYTE))) {
  793. UnlockConsole(Console);
  794. return STATUS_INVALID_PARAMETER;
  795. }
  796. AliasesBufferLength = a->AliasesBufferLength;
  797. if (a->Unicode) {
  798. AliasesBufferPtrW = a->AliasesBuffer;
  799. } else {
  800. AliasesBufferPtrA = a->AliasesBuffer;
  801. }
  802. a->AliasesBufferLength = 0;
  803. ExeAliasList = FindExe(Console,a->Exe,a->ExeLength,a->UnicodeExe);
  804. if (ExeAliasList) {
  805. ListHead = &ExeAliasList->AliasList;
  806. ListNext = ListHead->Flink;
  807. while (ListNext != ListHead) {
  808. Alias = CONTAINING_RECORD( ListNext, ALIAS, ListLink );
  809. if (a->Unicode) {
  810. if ((a->AliasesBufferLength + Alias->SourceLength + Alias->TargetLength + (2*sizeof(WCHAR)))
  811. <= AliasesBufferLength) {
  812. RtlCopyMemory(AliasesBufferPtrW,Alias->Source,Alias->SourceLength);
  813. AliasesBufferPtrW+=Alias->SourceLength/sizeof(WCHAR);
  814. *AliasesBufferPtrW++= (WCHAR)'=';
  815. RtlCopyMemory(AliasesBufferPtrW,Alias->Target,Alias->TargetLength);
  816. AliasesBufferPtrW+=Alias->TargetLength/sizeof(WCHAR);
  817. *AliasesBufferPtrW++= (WCHAR)'\0';
  818. a->AliasesBufferLength += Alias->SourceLength + Alias->TargetLength + (2*sizeof(WCHAR)); // +2 is for = and term null
  819. } else {
  820. UnlockConsole(Console);
  821. return STATUS_BUFFER_OVERFLOW;
  822. }
  823. } else {
  824. if ((a->AliasesBufferLength + ((Alias->SourceLength + Alias->TargetLength)/sizeof(WCHAR)) + (2*sizeof(CHAR)))
  825. <= AliasesBufferLength) {
  826. USHORT SourceLength,TargetLength;
  827. SourceLength = (USHORT)ConvertToOem(Console->CP,
  828. Alias->Source,
  829. Alias->SourceLength / sizeof(WCHAR),
  830. AliasesBufferPtrA,
  831. CHAR_COUNT(Alias->SourceLength)
  832. );
  833. AliasesBufferPtrA+=SourceLength;
  834. *AliasesBufferPtrA++ = '=';
  835. TargetLength = (USHORT)ConvertToOem(Console->CP,
  836. Alias->Target,
  837. Alias->TargetLength / sizeof(WCHAR),
  838. AliasesBufferPtrA,
  839. CHAR_COUNT(Alias->TargetLength)
  840. );
  841. AliasesBufferPtrA+=TargetLength;
  842. *AliasesBufferPtrA++= '\0';
  843. a->AliasesBufferLength += SourceLength + TargetLength + (2*sizeof(CHAR)); // +2 is for = and term null
  844. } else {
  845. UnlockConsole(Console);
  846. return STATUS_BUFFER_OVERFLOW;
  847. }
  848. }
  849. ListNext = ListNext->Flink;
  850. }
  851. }
  852. UnlockConsole(Console);
  853. return STATUS_SUCCESS;
  854. UNREFERENCED_PARAMETER(ReplyStatus);
  855. }
  856. DWORD
  857. SrvGetConsoleAliasExesLength(
  858. IN OUT PCSR_API_MSG m,
  859. IN OUT PCSR_REPLY_STATUS ReplyStatus
  860. )
  861. {
  862. PCONSOLE_GETALIASEXESLENGTH_MSG a = (PCONSOLE_GETALIASEXESLENGTH_MSG)&m->u.ApiMessageData;
  863. PCONSOLE_INFORMATION Console;
  864. PEXE_ALIAS_LIST AliasList;
  865. PLIST_ENTRY ListHead, ListNext;
  866. NTSTATUS Status;
  867. Status = ApiPreamble(a->ConsoleHandle,
  868. &Console
  869. );
  870. if (!NT_SUCCESS(Status)) {
  871. return Status;
  872. }
  873. a->AliasExesLength = 0;
  874. ListHead = &Console->ExeAliasList;
  875. ListNext = ListHead->Flink;
  876. while (ListNext != ListHead) {
  877. AliasList = CONTAINING_RECORD( ListNext, EXE_ALIAS_LIST, ListLink );
  878. a->AliasExesLength += AliasList->ExeLength + (1*sizeof(WCHAR)); // +1 for term null
  879. ListNext = ListNext->Flink;
  880. }
  881. if (!a->Unicode) {
  882. a->AliasExesLength /= sizeof(WCHAR);
  883. }
  884. UnlockConsole(Console);
  885. return STATUS_SUCCESS;
  886. UNREFERENCED_PARAMETER(ReplyStatus);
  887. }
  888. DWORD
  889. SrvGetConsoleAliasExes(
  890. IN OUT PCSR_API_MSG m,
  891. IN OUT PCSR_REPLY_STATUS ReplyStatus
  892. )
  893. {
  894. PCONSOLE_GETALIASEXES_MSG a = (PCONSOLE_GETALIASEXES_MSG)&m->u.ApiMessageData;
  895. PCONSOLE_INFORMATION Console;
  896. PEXE_ALIAS_LIST AliasList;
  897. PLIST_ENTRY ListHead, ListNext;
  898. DWORD AliasExesBufferLength;
  899. LPWSTR AliasExesBufferPtrW;
  900. LPSTR AliasExesBufferPtrA;
  901. NTSTATUS Status;
  902. Status = ApiPreamble(a->ConsoleHandle,
  903. &Console
  904. );
  905. if (!NT_SUCCESS(Status)) {
  906. return Status;
  907. }
  908. if (!CsrValidateMessageBuffer(m, &a->AliasExesBuffer, a->AliasExesBufferLength, sizeof(BYTE))) {
  909. UnlockConsole(Console);
  910. return STATUS_INVALID_PARAMETER;
  911. }
  912. AliasExesBufferLength = a->AliasExesBufferLength;
  913. if (a->Unicode) {
  914. AliasExesBufferPtrW = a->AliasExesBuffer;
  915. } else {
  916. AliasExesBufferPtrA = a->AliasExesBuffer;
  917. }
  918. a->AliasExesBufferLength = 0;
  919. ListHead = &Console->ExeAliasList;
  920. ListNext = ListHead->Flink;
  921. while (ListNext != ListHead) {
  922. AliasList = CONTAINING_RECORD( ListNext, EXE_ALIAS_LIST, ListLink );
  923. if (a->Unicode) {
  924. if ((a->AliasExesBufferLength + AliasList->ExeLength + (1*sizeof(WCHAR)))
  925. <= AliasExesBufferLength) {
  926. RtlCopyMemory(AliasExesBufferPtrW,AliasList->ExeName,AliasList->ExeLength);
  927. AliasExesBufferPtrW+=AliasList->ExeLength/sizeof(WCHAR);
  928. *AliasExesBufferPtrW++= (WCHAR)'\0';
  929. a->AliasExesBufferLength += AliasList->ExeLength + (1*sizeof(WCHAR)); // +1 is term null
  930. } else {
  931. UnlockConsole(Console);
  932. return STATUS_BUFFER_OVERFLOW;
  933. }
  934. } else {
  935. if ((a->AliasExesBufferLength + (AliasList->ExeLength/sizeof(WCHAR)) + (1*sizeof(CHAR)))
  936. <= AliasExesBufferLength) {
  937. USHORT Length;
  938. Length = (USHORT)ConvertToOem(Console->CP,
  939. AliasList->ExeName,
  940. AliasList->ExeLength / sizeof(WCHAR),
  941. AliasExesBufferPtrA,
  942. CHAR_COUNT(AliasList->ExeLength)
  943. );
  944. AliasExesBufferPtrA+=Length;
  945. *AliasExesBufferPtrA++= (WCHAR)'\0';
  946. a->AliasExesBufferLength += Length + (1*sizeof(CHAR)); // +1 is term null
  947. } else {
  948. UnlockConsole(Console);
  949. return STATUS_BUFFER_OVERFLOW;
  950. }
  951. }
  952. ListNext = ListNext->Flink;
  953. }
  954. UnlockConsole(Console);
  955. return STATUS_SUCCESS;
  956. UNREFERENCED_PARAMETER(ReplyStatus);
  957. }
  958. #define MAX_ARGS 9
  959. NTSTATUS
  960. MatchandCopyAlias(
  961. IN PCONSOLE_INFORMATION Console,
  962. IN PWCHAR Source,
  963. IN USHORT SourceLength,
  964. OUT PWCHAR TargetBuffer,
  965. IN OUT PUSHORT TargetLength,
  966. IN LPWSTR Exe,
  967. IN USHORT ExeLength,
  968. OUT PDWORD LineCount
  969. )
  970. /*++
  971. Routine Description:
  972. This routine matches the input string with an alias and copies the
  973. alias to the input buffer.
  974. Arguments:
  975. Source - string to match
  976. SourceLength - length of Source in bytes
  977. TargetBuffer - where to store matched string
  978. TargetLength - on input, contains size of TargetBuffer. On output,
  979. contains length of alias stored in TargetBuffer.
  980. SourceIsCommandLine - if true, source buffer is a command line, where
  981. the first blank separate token is to be check for an alias, and if
  982. it matches, replaced with the value of the alias. if false, then
  983. the source string is a null terminated alias name.
  984. LineCount - aliases can contain multiple commands. $T is the command
  985. separator
  986. Return Value:
  987. SUCCESS - match was found and alias was copied to buffer.
  988. --*/
  989. {
  990. PALIAS Alias;
  991. NTSTATUS Status = STATUS_SUCCESS;
  992. USHORT SourceUpToFirstBlank=0; // in chars
  993. PWCHAR Tmp;
  994. PEXE_ALIAS_LIST ExeAliasList;
  995. LPWSTR Args[MAX_ARGS];
  996. USHORT ArgsLength[MAX_ARGS]; // in bytes
  997. USHORT NumSourceArgs;
  998. LPWSTR SourcePtr;
  999. USHORT ArgCount,i,j,NewTargetLength;
  1000. USHORT SourceRemainderLength; // in chars
  1001. PWCHAR Buffer,TargetAlias;
  1002. PWCHAR TmpBuffer;
  1003. //
  1004. // alloc of exename may have failed.
  1005. //
  1006. if (Exe == NULL)
  1007. return STATUS_UNSUCCESSFUL;
  1008. //
  1009. // find exe
  1010. //
  1011. ExeAliasList = FindExe(Console,Exe,ExeLength,TRUE);
  1012. if (!ExeAliasList) {
  1013. return STATUS_UNSUCCESSFUL;
  1014. }
  1015. //
  1016. // find first blank
  1017. //
  1018. for (Tmp=Source,SourceUpToFirstBlank=0;
  1019. *Tmp!=(WCHAR)' ' && SourceUpToFirstBlank<(USHORT)(SourceLength/sizeof(WCHAR));
  1020. Tmp++,SourceUpToFirstBlank++) ;
  1021. //
  1022. // find char past first blank
  1023. //
  1024. j=SourceUpToFirstBlank;
  1025. while (j<(USHORT)(SourceLength/sizeof(WCHAR)) && *Tmp==(WCHAR)' ') {
  1026. Tmp++;
  1027. j++;
  1028. }
  1029. SourcePtr = Tmp;
  1030. SourceRemainderLength = (USHORT)((SourceLength/sizeof(WCHAR)) - j);
  1031. //
  1032. // find alias
  1033. //
  1034. Alias = FindAlias(ExeAliasList,Source,(USHORT)(SourceUpToFirstBlank*sizeof(WCHAR)));
  1035. if (!Alias) {
  1036. return STATUS_UNSUCCESSFUL;
  1037. }
  1038. TmpBuffer = ConsoleHeapAlloc(TMP_TAG, *TargetLength);
  1039. if (!TmpBuffer) {
  1040. return STATUS_NO_MEMORY;
  1041. }
  1042. //
  1043. // count args in target
  1044. //
  1045. ArgCount=0;
  1046. *LineCount=1;
  1047. Tmp=Alias->Target;
  1048. for (i=0;(USHORT)(i+1)<(USHORT)(Alias->TargetLength/sizeof(WCHAR));i++) {
  1049. if (*Tmp == (WCHAR)'$' && *(Tmp+1) >= (WCHAR)'1' && *(Tmp+1) <= (WCHAR)'9') {
  1050. USHORT ArgNum = *(Tmp+1) - (WCHAR)'0';
  1051. if (ArgNum > ArgCount) {
  1052. ArgCount = ArgNum;
  1053. }
  1054. Tmp++;
  1055. i++;
  1056. } else if (*Tmp == (WCHAR)'$' && *(Tmp+1) == (WCHAR)'*') {
  1057. if (ArgCount==0) {
  1058. ArgCount = 1;
  1059. }
  1060. Tmp++;
  1061. i++;
  1062. }
  1063. Tmp++;
  1064. }
  1065. //
  1066. // package up space separated strings in source into array
  1067. // of args
  1068. //
  1069. //
  1070. NumSourceArgs=0;
  1071. Tmp = SourcePtr;
  1072. for (i=0,j=0;i<ArgCount;i++) {
  1073. if (j<SourceRemainderLength) {
  1074. Args[NumSourceArgs] = Tmp;
  1075. ArgsLength[NumSourceArgs] = 0;
  1076. while (j++<SourceRemainderLength && *Tmp++ != (WCHAR)' ') {
  1077. ArgsLength[NumSourceArgs] += sizeof(WCHAR);
  1078. }
  1079. while (j<SourceRemainderLength && *Tmp == (WCHAR)' ') {
  1080. j++;
  1081. Tmp++;
  1082. }
  1083. NumSourceArgs++;
  1084. } else {
  1085. break;
  1086. }
  1087. }
  1088. //
  1089. // put together the target string
  1090. //
  1091. // while (target)
  1092. // if ($)
  1093. // if arg && arg# <= ArgCount
  1094. // copy arg
  1095. // else if *
  1096. // copy arg
  1097. // else
  1098. // replace with < > etc
  1099. // else
  1100. // copy text up to next ' '
  1101. //
  1102. Buffer = TmpBuffer;
  1103. NewTargetLength = 2*sizeof(WCHAR); // for CRLF
  1104. TargetAlias=Alias->Target;
  1105. for (i=0;i<(USHORT)(Alias->TargetLength/sizeof(WCHAR));i++) {
  1106. if (NewTargetLength >= *TargetLength) {
  1107. *TargetLength = NewTargetLength;
  1108. Status = STATUS_BUFFER_TOO_SMALL;
  1109. break;
  1110. }
  1111. if (*TargetAlias == (WCHAR)'$' && (USHORT)(i+1)<(USHORT)(Alias->TargetLength/sizeof(WCHAR))) {
  1112. TargetAlias++;
  1113. i++;
  1114. if (*TargetAlias >= (WCHAR)'1' && *TargetAlias <= (WCHAR)'9') {
  1115. //
  1116. // do numbered parameter substitution
  1117. //
  1118. USHORT ArgNumber;
  1119. ArgNumber = (USHORT)(*TargetAlias - (WCHAR)'1');
  1120. if (ArgNumber < NumSourceArgs) {
  1121. if ((NewTargetLength+ArgsLength[ArgNumber])<=*TargetLength) {
  1122. RtlCopyMemory(Buffer,Args[ArgNumber],ArgsLength[ArgNumber]);
  1123. Buffer+=ArgsLength[ArgNumber]/sizeof(WCHAR);
  1124. NewTargetLength+=ArgsLength[ArgNumber];
  1125. } else {
  1126. Status = STATUS_BUFFER_TOO_SMALL;
  1127. break;
  1128. }
  1129. }
  1130. } else if (*TargetAlias == (WCHAR)'*') {
  1131. //
  1132. // do * parameter substitution
  1133. //
  1134. if (NumSourceArgs) {
  1135. if ((USHORT)(NewTargetLength+(SourceRemainderLength*sizeof(WCHAR)))<=*TargetLength) {
  1136. RtlCopyMemory(Buffer,Args[0],SourceRemainderLength*sizeof(WCHAR));
  1137. Buffer+=SourceRemainderLength;
  1138. NewTargetLength+=SourceRemainderLength*sizeof(WCHAR);
  1139. } else {
  1140. Status = STATUS_BUFFER_TOO_SMALL;
  1141. break;
  1142. }
  1143. }
  1144. } else if (*TargetAlias == (WCHAR)'l' || *TargetAlias == (WCHAR)'L') {
  1145. //
  1146. // do < substitution
  1147. //
  1148. *Buffer++ = (WCHAR)'<';
  1149. NewTargetLength+=sizeof(WCHAR);
  1150. } else if (*TargetAlias == (WCHAR)'g' || *TargetAlias == (WCHAR)'G') {
  1151. //
  1152. // do > substitution
  1153. //
  1154. *Buffer++ = (WCHAR)'>';
  1155. NewTargetLength+=sizeof(WCHAR);
  1156. } else if (*TargetAlias == (WCHAR)'b' || *TargetAlias == (WCHAR)'B') {
  1157. //
  1158. // do | substitution
  1159. //
  1160. *Buffer++ = (WCHAR)'|';
  1161. NewTargetLength+=sizeof(WCHAR);
  1162. } else if (*TargetAlias == (WCHAR)'t' || *TargetAlias == (WCHAR)'T') {
  1163. //
  1164. // do newline substitution
  1165. //
  1166. if ((USHORT)(NewTargetLength+(sizeof(WCHAR)*2))>*TargetLength) {
  1167. Status = STATUS_BUFFER_TOO_SMALL;
  1168. }
  1169. *LineCount += 1;
  1170. *Buffer++ = UNICODE_CARRIAGERETURN;
  1171. *Buffer++ = UNICODE_LINEFEED;
  1172. NewTargetLength+=sizeof(WCHAR)*2;
  1173. } else {
  1174. //
  1175. // copy $X
  1176. //
  1177. *Buffer++ = (WCHAR)'$';
  1178. NewTargetLength+=sizeof(WCHAR);
  1179. *Buffer++ = *TargetAlias;
  1180. NewTargetLength+=sizeof(WCHAR);
  1181. }
  1182. TargetAlias++;
  1183. } else {
  1184. //
  1185. // copy char
  1186. //
  1187. *Buffer++ = *TargetAlias++;
  1188. NewTargetLength+=sizeof(WCHAR);
  1189. }
  1190. }
  1191. *Buffer++ = UNICODE_CARRIAGERETURN;
  1192. *Buffer++ = UNICODE_LINEFEED;
  1193. RtlCopyMemory(TargetBuffer,TmpBuffer,NewTargetLength);
  1194. ConsoleHeapFree(TmpBuffer);
  1195. *TargetLength = NewTargetLength;
  1196. return Status;
  1197. }
  1198. DWORD
  1199. SrvExpungeConsoleCommandHistory(
  1200. IN OUT PCSR_API_MSG m,
  1201. IN OUT PCSR_REPLY_STATUS ReplyStatus
  1202. )
  1203. {
  1204. PCONSOLE_EXPUNGECOMMANDHISTORY_MSG a = (PCONSOLE_EXPUNGECOMMANDHISTORY_MSG)&m->u.ApiMessageData;
  1205. PCONSOLE_INFORMATION Console;
  1206. NTSTATUS Status;
  1207. Status = ApiPreamble(a->ConsoleHandle,
  1208. &Console
  1209. );
  1210. if (!NT_SUCCESS(Status)) {
  1211. return Status;
  1212. }
  1213. if (!CsrValidateMessageBuffer(m, &a->Exe, a->ExeLength, sizeof(BYTE))) {
  1214. UnlockConsole(Console);
  1215. return STATUS_INVALID_PARAMETER;
  1216. }
  1217. EmptyCommandHistory(FindExeCommandHistory(Console,
  1218. a->Exe,
  1219. a->ExeLength,
  1220. a->UnicodeExe));
  1221. UnlockConsole(Console);
  1222. return STATUS_SUCCESS;
  1223. UNREFERENCED_PARAMETER(ReplyStatus);
  1224. }
  1225. DWORD
  1226. SrvSetConsoleNumberOfCommands(
  1227. IN OUT PCSR_API_MSG m,
  1228. IN OUT PCSR_REPLY_STATUS ReplyStatus
  1229. )
  1230. {
  1231. PCONSOLE_SETNUMBEROFCOMMANDS_MSG a = (PCONSOLE_SETNUMBEROFCOMMANDS_MSG)&m->u.ApiMessageData;
  1232. PCONSOLE_INFORMATION Console;
  1233. NTSTATUS Status;
  1234. Status = ApiPreamble(a->ConsoleHandle,
  1235. &Console
  1236. );
  1237. if (!NT_SUCCESS(Status)) {
  1238. return Status;
  1239. }
  1240. if (!CsrValidateMessageBuffer(m, &a->Exe, a->ExeLength, sizeof(BYTE))) {
  1241. UnlockConsole(Console);
  1242. return STATUS_INVALID_PARAMETER;
  1243. }
  1244. ReallocCommandHistory(Console,
  1245. FindExeCommandHistory(Console,
  1246. a->Exe,
  1247. a->ExeLength,
  1248. a->UnicodeExe),
  1249. a->NumCommands
  1250. );
  1251. UnlockConsole(Console);
  1252. return STATUS_SUCCESS;
  1253. UNREFERENCED_PARAMETER(ReplyStatus);
  1254. }
  1255. DWORD
  1256. SrvGetConsoleCommandHistoryLength(
  1257. IN OUT PCSR_API_MSG m,
  1258. IN OUT PCSR_REPLY_STATUS ReplyStatus
  1259. )
  1260. {
  1261. PCONSOLE_GETCOMMANDHISTORYLENGTH_MSG a = (PCONSOLE_GETCOMMANDHISTORYLENGTH_MSG)&m->u.ApiMessageData;
  1262. PCONSOLE_INFORMATION Console;
  1263. NTSTATUS Status;
  1264. SHORT i;
  1265. PCOMMAND_HISTORY CommandHistory;
  1266. Status = ApiPreamble(a->ConsoleHandle,
  1267. &Console
  1268. );
  1269. if (!NT_SUCCESS(Status)) {
  1270. return Status;
  1271. }
  1272. if (!CsrValidateMessageBuffer(m, &a->Exe, a->ExeLength, sizeof(BYTE))) {
  1273. UnlockConsole(Console);
  1274. return STATUS_INVALID_PARAMETER;
  1275. }
  1276. a->CommandHistoryLength=0;
  1277. CommandHistory=FindExeCommandHistory(Console,
  1278. a->Exe,
  1279. a->ExeLength,
  1280. a->UnicodeExe);
  1281. if (CommandHistory) {
  1282. for (i=0;i<CommandHistory->NumberOfCommands;i++) {
  1283. a->CommandHistoryLength+=CommandHistory->Commands[i]->CommandLength+sizeof(WCHAR);
  1284. }
  1285. }
  1286. if (!a->Unicode) {
  1287. a->CommandHistoryLength /= sizeof(WCHAR);
  1288. }
  1289. UnlockConsole(Console);
  1290. return STATUS_SUCCESS;
  1291. UNREFERENCED_PARAMETER(ReplyStatus);
  1292. }
  1293. DWORD
  1294. SrvGetConsoleCommandHistory(
  1295. IN OUT PCSR_API_MSG m,
  1296. IN OUT PCSR_REPLY_STATUS ReplyStatus
  1297. )
  1298. {
  1299. PCONSOLE_GETCOMMANDHISTORY_MSG a = (PCONSOLE_GETCOMMANDHISTORY_MSG)&m->u.ApiMessageData;
  1300. PCONSOLE_INFORMATION Console;
  1301. NTSTATUS Status;
  1302. SHORT i,CommandHistoryLength;
  1303. PCOMMAND_HISTORY CommandHistory;
  1304. PWCHAR CommandBufferW;
  1305. PCHAR CommandBufferA;
  1306. Status = ApiPreamble(a->ConsoleHandle,
  1307. &Console
  1308. );
  1309. if (!NT_SUCCESS(Status)) {
  1310. return Status;
  1311. }
  1312. if (!CsrValidateMessageBuffer(m, &a->CommandBuffer, a->CommandBufferLength, sizeof(BYTE)) ||
  1313. !CsrValidateMessageBuffer(m, &a->Exe, a->ExeLength, sizeof(BYTE))) {
  1314. UnlockConsole(Console);
  1315. return STATUS_INVALID_PARAMETER;
  1316. }
  1317. if (a->Unicode) {
  1318. CommandBufferW=a->CommandBuffer;
  1319. } else {
  1320. CommandBufferA=a->CommandBuffer;
  1321. }
  1322. CommandHistoryLength=0;
  1323. CommandHistory=FindExeCommandHistory(Console,
  1324. a->Exe,
  1325. a->ExeLength,
  1326. a->UnicodeExe);
  1327. if (CommandHistory) {
  1328. for (i=0;i<CommandHistory->NumberOfCommands;i++) {
  1329. if (a->Unicode) {
  1330. if ((CommandHistoryLength+CommandHistory->Commands[i]->CommandLength+sizeof(WCHAR)) <= a->CommandBufferLength) {
  1331. RtlCopyMemory(CommandBufferW,CommandHistory->Commands[i]->Command,CommandHistory->Commands[i]->CommandLength);
  1332. CommandBufferW+=CommandHistory->Commands[i]->CommandLength/sizeof(WCHAR);
  1333. *CommandBufferW++=(WCHAR)'\0';
  1334. CommandHistoryLength+=CommandHistory->Commands[i]->CommandLength+sizeof(WCHAR);
  1335. } else {
  1336. Status = STATUS_BUFFER_OVERFLOW;
  1337. break;
  1338. }
  1339. } else {
  1340. if ((CommandHistoryLength+(CommandHistory->Commands[i]->CommandLength/sizeof(WCHAR))+sizeof(CHAR)) <= a->CommandBufferLength) {
  1341. USHORT Length;
  1342. Length = (USHORT)ConvertToOem(Console->CP,
  1343. CommandHistory->Commands[i]->Command,
  1344. CommandHistory->Commands[i]->CommandLength / sizeof(WCHAR),
  1345. CommandBufferA,
  1346. CHAR_COUNT(CommandHistory->Commands[i]->CommandLength)
  1347. );
  1348. CommandBufferA+=Length;
  1349. *CommandBufferA++=(WCHAR)'\0';
  1350. CommandHistoryLength+=CommandHistory->Commands[i]->CommandLength+sizeof(WCHAR);
  1351. } else {
  1352. Status = STATUS_BUFFER_OVERFLOW;
  1353. break;
  1354. }
  1355. }
  1356. }
  1357. }
  1358. a->CommandBufferLength=CommandHistoryLength;
  1359. UnlockConsole(Console);
  1360. return Status;
  1361. UNREFERENCED_PARAMETER(ReplyStatus);
  1362. }
  1363. DWORD
  1364. SrvSetConsoleCommandHistoryMode(
  1365. IN OUT PCSR_API_MSG m,
  1366. IN OUT PCSR_REPLY_STATUS ReplyStatus
  1367. )
  1368. {
  1369. PCONSOLE_SETCOMMANDHISTORYMODE_MSG a = (PCONSOLE_SETCOMMANDHISTORYMODE_MSG)&m->u.ApiMessageData;
  1370. PCONSOLE_INFORMATION Console;
  1371. NTSTATUS Status;
  1372. Status = ApiPreamble(a->ConsoleHandle,
  1373. &Console
  1374. );
  1375. if (!NT_SUCCESS(Status)) {
  1376. return Status;
  1377. }
  1378. Console->InsertMode = (BOOLEAN) (a->Flags != CONSOLE_OVERSTRIKE);
  1379. UnlockConsole(Console);
  1380. return STATUS_SUCCESS;
  1381. UNREFERENCED_PARAMETER(ReplyStatus);
  1382. }
  1383. PCOMMAND_HISTORY
  1384. ReallocCommandHistory(
  1385. IN PCONSOLE_INFORMATION Console,
  1386. IN PCOMMAND_HISTORY CurrentCommandHistory,
  1387. IN DWORD NumCommands)
  1388. {
  1389. PCOMMAND_HISTORY History;
  1390. int i;
  1391. /*
  1392. * To protect ourselves from overflow, a limit of 0xFFFF is put on the
  1393. * size of the command history.
  1394. */
  1395. if (CurrentCommandHistory == NULL ||
  1396. CurrentCommandHistory->MaximumNumberOfCommands == (SHORT)NumCommands ||
  1397. NumCommands > 0xFFFF) {
  1398. return CurrentCommandHistory;
  1399. }
  1400. History = ConsoleHeapAlloc(HISTORY_TAG,
  1401. sizeof(COMMAND_HISTORY) + NumCommands * sizeof(PCOMMAND));
  1402. if (History == NULL) {
  1403. return CurrentCommandHistory;
  1404. }
  1405. *History = *CurrentCommandHistory;
  1406. History->Flags |= CLE_RESET;
  1407. History->NumberOfCommands = min(History->NumberOfCommands, (SHORT)NumCommands);
  1408. History->LastAdded = History->NumberOfCommands - 1;
  1409. History->LastDisplayed = History->NumberOfCommands - 1;
  1410. History->FirstCommand = 0;
  1411. History->MaximumNumberOfCommands = (SHORT)NumCommands;
  1412. for (i = 0; i < History->NumberOfCommands; i++) {
  1413. History->Commands[i] = CurrentCommandHistory->Commands[COMMAND_NUM_TO_INDEX(i, CurrentCommandHistory)];
  1414. }
  1415. for (; i < CurrentCommandHistory->NumberOfCommands; i++) {
  1416. ConsoleHeapFree(CurrentCommandHistory->Commands[COMMAND_NUM_TO_INDEX(i, CurrentCommandHistory)]);
  1417. }
  1418. RemoveEntryList(&CurrentCommandHistory->ListLink);
  1419. InitializeListHead(&History->PopupList);
  1420. InsertHeadList(&Console->CommandHistoryList,&History->ListLink);
  1421. ConsoleHeapFree(CurrentCommandHistory);
  1422. return History;
  1423. }
  1424. PCOMMAND_HISTORY
  1425. FindExeCommandHistory(
  1426. IN PCONSOLE_INFORMATION Console,
  1427. IN PVOID AppName,
  1428. IN DWORD AppNameLength,
  1429. IN BOOLEAN Unicode
  1430. )
  1431. {
  1432. PCOMMAND_HISTORY History;
  1433. PLIST_ENTRY ListHead, ListNext;
  1434. PWCHAR AppNamePtr;
  1435. if (!Unicode) {
  1436. AppNamePtr = ConsoleHeapAlloc(TMP_TAG, AppNameLength * sizeof(WCHAR));
  1437. if (AppNamePtr == NULL) {
  1438. return NULL;
  1439. }
  1440. AppNameLength = ConvertInputToUnicode(Console->CP,
  1441. AppName,
  1442. AppNameLength,
  1443. AppNamePtr,
  1444. AppNameLength);
  1445. AppNameLength *= 2;
  1446. } else {
  1447. AppNamePtr = AppName;
  1448. }
  1449. ListHead = &Console->CommandHistoryList;
  1450. ListNext = ListHead->Flink;
  1451. while (ListNext != ListHead) {
  1452. History = CONTAINING_RECORD( ListNext, COMMAND_HISTORY, ListLink );
  1453. ListNext = ListNext->Flink;
  1454. if (History->Flags & CLE_ALLOCATED &&
  1455. !my_wcsncmpi(History->AppName,AppNamePtr,(USHORT)AppNameLength)) {
  1456. if (!Unicode) {
  1457. ConsoleHeapFree(AppNamePtr);
  1458. }
  1459. return History;
  1460. }
  1461. }
  1462. if (!Unicode) {
  1463. ConsoleHeapFree(AppNamePtr);
  1464. }
  1465. return NULL;
  1466. }
  1467. PCOMMAND_HISTORY
  1468. AllocateCommandHistory(
  1469. IN PCONSOLE_INFORMATION Console,
  1470. IN DWORD AppNameLength,
  1471. IN PWCHAR AppName,
  1472. IN HANDLE ProcessHandle
  1473. )
  1474. /*++
  1475. Routine Description:
  1476. This routine returns the LRU command history buffer, or the command history
  1477. buffer that corresponds to the app name.
  1478. Arguments:
  1479. Console - pointer to console.
  1480. Return Value:
  1481. Pointer to command history buffer. if none are available, returns NULL.
  1482. --*/
  1483. {
  1484. PCOMMAND_HISTORY History,BestCandidate;
  1485. PLIST_ENTRY ListHead, ListNext;
  1486. BOOL SameApp;
  1487. //
  1488. // Reuse a history buffer. The buffer must be !CLE_ALLOCATED.
  1489. // If possible, the buffer should have the same app name.
  1490. //
  1491. ListHead = &Console->CommandHistoryList;
  1492. ListNext = ListHead->Blink;
  1493. BestCandidate = NULL;
  1494. SameApp = FALSE;
  1495. while (ListNext != ListHead) {
  1496. History = CONTAINING_RECORD( ListNext, COMMAND_HISTORY, ListLink );
  1497. ListNext = ListNext->Blink;
  1498. if ((History->Flags & CLE_ALLOCATED) == 0) {
  1499. //
  1500. // use LRU history buffer with same app name
  1501. //
  1502. if (History->AppName && !my_wcsncmpi(History->AppName,AppName,(USHORT)AppNameLength)) {
  1503. BestCandidate = History;
  1504. SameApp = TRUE;
  1505. break;
  1506. }
  1507. //
  1508. // second best choice is LRU history buffer
  1509. //
  1510. if (BestCandidate == NULL) {
  1511. BestCandidate = History;
  1512. }
  1513. }
  1514. }
  1515. //
  1516. // if there isn't a free buffer for the app name and the maximum number of
  1517. // command history buffers hasn't been allocated, allocate a new one.
  1518. //
  1519. if (!SameApp && Console->NumCommandHistories < Console->MaxCommandHistories) {
  1520. History = ConsoleHeapAlloc(HISTORY_TAG,
  1521. sizeof(COMMAND_HISTORY) + Console->CommandHistorySize * sizeof(PCOMMAND));
  1522. if (History == NULL) {
  1523. return NULL;
  1524. }
  1525. History->AppName = ConsoleHeapAlloc(HISTORY_TAG, AppNameLength);
  1526. if (History->AppName == NULL) {
  1527. ConsoleHeapFree(History);
  1528. return NULL;
  1529. }
  1530. RtlCopyMemory(History->AppName,AppName,AppNameLength);
  1531. History->Flags = CLE_ALLOCATED;
  1532. History->NumberOfCommands = 0;
  1533. History->LastAdded = -1;
  1534. History->LastDisplayed = -1;
  1535. History->FirstCommand = 0;
  1536. History->MaximumNumberOfCommands = Console->CommandHistorySize;
  1537. InsertHeadList(&Console->CommandHistoryList,&History->ListLink);
  1538. Console->NumCommandHistories+=1;
  1539. History->ProcessHandle = ProcessHandle;
  1540. InitializeListHead(&History->PopupList);
  1541. return History;
  1542. }
  1543. //
  1544. // if the app name doesn't match, copy in the new app name and free the old commands.
  1545. //
  1546. if (BestCandidate) {
  1547. History = BestCandidate;
  1548. UserAssert(CLE_NO_POPUPS(History));
  1549. if (!SameApp) {
  1550. SHORT i;
  1551. if (History->AppName) {
  1552. DBGPRINT(("Reusing %ls command history\n", History->AppName));
  1553. ConsoleHeapFree(History->AppName);
  1554. }
  1555. for (i=0;i<History->NumberOfCommands;i++) {
  1556. ConsoleHeapFree(History->Commands[i]);
  1557. }
  1558. History->NumberOfCommands = 0;
  1559. History->LastAdded = -1;
  1560. History->LastDisplayed = -1;
  1561. History->FirstCommand = 0;
  1562. History->AppName = ConsoleHeapAlloc(HISTORY_TAG, AppNameLength);
  1563. if (History->AppName == NULL) {
  1564. History->Flags &= ~CLE_ALLOCATED;
  1565. return NULL;
  1566. }
  1567. RtlCopyMemory(History->AppName,AppName,AppNameLength);
  1568. }
  1569. History->ProcessHandle = ProcessHandle;
  1570. History->Flags |= CLE_ALLOCATED;
  1571. //
  1572. // move to the front of the list
  1573. //
  1574. RemoveEntryList(&BestCandidate->ListLink);
  1575. InsertHeadList(&Console->CommandHistoryList,&BestCandidate->ListLink);
  1576. }
  1577. return BestCandidate;
  1578. }
  1579. NTSTATUS
  1580. BeginPopup(
  1581. IN PSCREEN_INFORMATION ScreenInfo,
  1582. IN PCOMMAND_HISTORY CommandHistory,
  1583. IN COORD PopupSize
  1584. )
  1585. {
  1586. COORD Origin;
  1587. COORD Size;
  1588. PCLE_POPUP Popup;
  1589. SMALL_RECT TargetRect;
  1590. // determine popup dimensions
  1591. Size = PopupSize;
  1592. Size.X += 2; // add borders
  1593. Size.Y += 2; // add borders
  1594. if (Size.X >= (SHORT)(CONSOLE_WINDOW_SIZE_X(ScreenInfo))) {
  1595. Size.X = (SHORT)(CONSOLE_WINDOW_SIZE_X(ScreenInfo));
  1596. }
  1597. if (Size.Y >= (SHORT)(CONSOLE_WINDOW_SIZE_Y(ScreenInfo))) {
  1598. Size.Y = (SHORT)(CONSOLE_WINDOW_SIZE_Y(ScreenInfo));
  1599. }
  1600. // make sure there's enough room for the popup borders
  1601. if (Size.X < 2 || Size.Y < 2) {
  1602. return STATUS_BUFFER_TOO_SMALL;
  1603. }
  1604. // determine origin. center popup on window
  1605. Origin.X = (SHORT)((CONSOLE_WINDOW_SIZE_X(ScreenInfo) - Size.X) / 2 + ScreenInfo->Window.Left);
  1606. Origin.Y = (SHORT)((CONSOLE_WINDOW_SIZE_Y(ScreenInfo) - Size.Y) / 2 + ScreenInfo->Window.Top);
  1607. // allocate a popup structure
  1608. Popup = ConsoleHeapAlloc(TMP_TAG, sizeof(CLE_POPUP));
  1609. if (Popup == NULL) {
  1610. return STATUS_NO_MEMORY;
  1611. }
  1612. // allocate a buffer
  1613. #if !defined(FE_SB)
  1614. Popup->OldContents = ConsoleHeapAlloc(TMP_TAG, Size.X * Size.Y * sizeof(CHAR_INFO));
  1615. #else
  1616. Popup->OldScreenSize = ScreenInfo->ScreenBufferSize;
  1617. Popup->OldContents = ConsoleHeapAlloc(TMP_TAG, Popup->OldScreenSize.X * Size.Y * sizeof(CHAR_INFO));
  1618. #endif
  1619. if (Popup->OldContents == NULL) {
  1620. ConsoleHeapFree(Popup);
  1621. return STATUS_NO_MEMORY;
  1622. }
  1623. if ((ScreenInfo->Flags & CONSOLE_OEMFONT_DISPLAY) &&
  1624. !(ScreenInfo->Console->FullScreenFlags & CONSOLE_FULLSCREEN)) {
  1625. Popup->Flags |= CLEPF_FALSE_UNICODE;
  1626. } else {
  1627. Popup->Flags &= ~CLEPF_FALSE_UNICODE;
  1628. }
  1629. //
  1630. // fill in popup structure
  1631. //
  1632. InsertHeadList(&CommandHistory->PopupList,&Popup->ListLink);
  1633. Popup->Region.Left = Origin.X;
  1634. Popup->Region.Top = Origin.Y;
  1635. Popup->Region.Right = (SHORT)(Origin.X + Size.X - 1);
  1636. Popup->Region.Bottom = (SHORT)(Origin.Y + Size.Y - 1);
  1637. Popup->Attributes = ScreenInfo->PopupAttributes;
  1638. Popup->BottomIndex = COMMAND_INDEX_TO_NUM(CommandHistory->LastDisplayed,CommandHistory);
  1639. //
  1640. // copy old contents
  1641. //
  1642. #if !defined(FE_SB)
  1643. TargetRect = Popup->Region;
  1644. #else
  1645. TargetRect.Left = 0;
  1646. TargetRect.Top = Popup->Region.Top;
  1647. TargetRect.Right = Popup->OldScreenSize.X - 1;
  1648. TargetRect.Bottom = Popup->Region.Bottom;
  1649. #endif
  1650. ReadScreenBuffer(ScreenInfo,
  1651. Popup->OldContents,
  1652. &TargetRect);
  1653. ScreenInfo->Console->PopupCount++;
  1654. DrawCommandListBorder(Popup,ScreenInfo);
  1655. return STATUS_SUCCESS;
  1656. }
  1657. NTSTATUS
  1658. EndPopup(
  1659. IN PSCREEN_INFORMATION ScreenInfo,
  1660. IN PCOMMAND_HISTORY CommandHistory)
  1661. {
  1662. COORD Size;
  1663. SMALL_RECT SourceRect;
  1664. PCLE_POPUP Popup;
  1665. UserAssert(!CLE_NO_POPUPS(CommandHistory));
  1666. if (CLE_NO_POPUPS(CommandHistory)) {
  1667. return STATUS_UNSUCCESSFUL;
  1668. }
  1669. ConsoleHideCursor(ScreenInfo);
  1670. Popup = CONTAINING_RECORD( CommandHistory->PopupList.Flink, CLE_POPUP, ListLink );
  1671. //
  1672. // restore previous contents to screen
  1673. //
  1674. #if !defined(FE_SB)
  1675. Size.X = (SHORT)(Popup->Region.Right - Popup->Region.Left + 1);
  1676. Size.Y = (SHORT)(Popup->Region.Bottom - Popup->Region.Top + 1);
  1677. SourceRect = Popup->Region;
  1678. #else
  1679. Size.X = Popup->OldScreenSize.X;
  1680. Size.Y = (SHORT)(Popup->Region.Bottom - Popup->Region.Top + 1);
  1681. SourceRect.Left = 0;
  1682. SourceRect.Top = Popup->Region.Top;
  1683. SourceRect.Right = Popup->OldScreenSize.X - 1;
  1684. SourceRect.Bottom = Popup->Region.Bottom;
  1685. #endif
  1686. if ((ScreenInfo->Flags & CONSOLE_OEMFONT_DISPLAY) &&
  1687. !(ScreenInfo->Console->FullScreenFlags & CONSOLE_FULLSCREEN)) {
  1688. /*
  1689. * Screen buffer wants fake Unicode
  1690. */
  1691. if (!(Popup->Flags & CLEPF_FALSE_UNICODE)) {
  1692. #if !defined(FE_SB)
  1693. TranslateOutputToAnsiUnicode(ScreenInfo->Console,
  1694. Popup->OldContents, Size);
  1695. #else
  1696. TranslateOutputToAnsiUnicode(ScreenInfo->Console,
  1697. Popup->OldContents, Size,
  1698. NULL);
  1699. #endif
  1700. }
  1701. } else {
  1702. /*
  1703. * Screen buffer wants real Unicode
  1704. */
  1705. if (Popup->Flags & CLEPF_FALSE_UNICODE) {
  1706. #if !defined(FE_SB)
  1707. TranslateOutputToOemUnicode(ScreenInfo->Console,
  1708. Popup->OldContents, Size);
  1709. #else
  1710. TranslateOutputToOemUnicode(ScreenInfo->Console,
  1711. Popup->OldContents, Size, FALSE);
  1712. #endif
  1713. }
  1714. }
  1715. WriteScreenBuffer(ScreenInfo,
  1716. Popup->OldContents,
  1717. &SourceRect
  1718. );
  1719. WriteToScreen(ScreenInfo,
  1720. &SourceRect
  1721. );
  1722. ConsoleShowCursor(ScreenInfo);
  1723. //
  1724. // free popup structure
  1725. //
  1726. RemoveEntryList(&Popup->ListLink);
  1727. ConsoleHeapFree(Popup->OldContents);
  1728. ConsoleHeapFree(Popup);
  1729. ScreenInfo->Console->PopupCount--;
  1730. return STATUS_SUCCESS;
  1731. }
  1732. VOID
  1733. CleanUpPopups(
  1734. IN PCOOKED_READ_DATA CookedReadData
  1735. )
  1736. {
  1737. PCOMMAND_HISTORY CommandHistory;
  1738. CommandHistory = CookedReadData->CommandHistory;
  1739. if (!CommandHistory)
  1740. return;
  1741. while (!CLE_NO_POPUPS(CommandHistory)) {
  1742. EndPopup(CookedReadData->ScreenInfo,CommandHistory);
  1743. }
  1744. }
  1745. VOID
  1746. DeleteCommandLine(
  1747. IN OUT PCOOKED_READ_DATA CookedReadData,
  1748. IN BOOL UpdateFields
  1749. )
  1750. {
  1751. DWORD CharsToWrite = CookedReadData->NumberOfVisibleChars;
  1752. COORD Coord = CookedReadData->OriginalCursorPosition;
  1753. //
  1754. // catch the case where the current command has scrolled off the
  1755. // top of the screen.
  1756. //
  1757. if (Coord.Y < 0) {
  1758. CharsToWrite += CookedReadData->ScreenInfo->ScreenBufferSize.X * Coord.Y;
  1759. CharsToWrite += CookedReadData->OriginalCursorPosition.X; // account for prompt
  1760. CookedReadData->OriginalCursorPosition.X = 0;
  1761. CookedReadData->OriginalCursorPosition.Y = 0;
  1762. Coord.X = 0;
  1763. Coord.Y = 0;
  1764. }
  1765. #if defined(FE_SB)
  1766. if (CONSOLE_IS_DBCS_OUTPUTCP(CookedReadData->ScreenInfo->Console) &&
  1767. !CheckBisectStringW(CookedReadData->ScreenInfo,
  1768. CookedReadData->ScreenInfo->Console->CP,
  1769. CookedReadData->BackupLimit,
  1770. CharsToWrite,
  1771. CookedReadData->ScreenInfo->ScreenBufferSize.X
  1772. -CookedReadData->OriginalCursorPosition.X
  1773. )) {
  1774. CharsToWrite++;
  1775. }
  1776. #endif
  1777. FillOutput(CookedReadData->ScreenInfo,
  1778. (WCHAR)' ',
  1779. Coord,
  1780. CONSOLE_FALSE_UNICODE, // faster than real unicode
  1781. &CharsToWrite
  1782. );
  1783. if (UpdateFields) {
  1784. CookedReadData->BufPtr=CookedReadData->BackupLimit;
  1785. CookedReadData->BytesRead=0;
  1786. CookedReadData->CurrentPosition=0;
  1787. CookedReadData->NumberOfVisibleChars = 0;
  1788. }
  1789. SetCursorPosition(CookedReadData->ScreenInfo,
  1790. CookedReadData->OriginalCursorPosition,
  1791. TRUE
  1792. );
  1793. }
  1794. VOID
  1795. RedrawCommandLine(
  1796. IN OUT PCOOKED_READ_DATA CookedReadData
  1797. )
  1798. {
  1799. NTSTATUS Status;
  1800. COORD CursorPosition;
  1801. SHORT ScrollY=0;
  1802. if (CookedReadData->Echo) {
  1803. //
  1804. // Draw the command line
  1805. //
  1806. CookedReadData->OriginalCursorPosition = CookedReadData->ScreenInfo->BufferInfo.TextInfo.CursorPosition;
  1807. Status = WriteCharsFromInput(CookedReadData->ScreenInfo,
  1808. CookedReadData->BackupLimit,
  1809. CookedReadData->BackupLimit,
  1810. CookedReadData->BackupLimit,
  1811. &CookedReadData->BytesRead,
  1812. &CookedReadData->NumberOfVisibleChars,
  1813. CookedReadData->OriginalCursorPosition.X,
  1814. WC_DESTRUCTIVE_BACKSPACE | WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
  1815. &ScrollY);
  1816. UserAssert(NT_SUCCESS(Status));
  1817. CookedReadData->OriginalCursorPosition.Y += ScrollY;
  1818. //
  1819. // Move the cursor back to the right position
  1820. //
  1821. CursorPosition = CookedReadData->OriginalCursorPosition;
  1822. CursorPosition.X += (SHORT)RetrieveTotalNumberOfSpaces(CookedReadData->OriginalCursorPosition.X,
  1823. CookedReadData->BackupLimit,
  1824. CookedReadData->CurrentPosition,
  1825. CookedReadData->ScreenInfo->Console);
  1826. if (CheckBisectStringW(CookedReadData->ScreenInfo,
  1827. CookedReadData->ScreenInfo->Console->CP,
  1828. CookedReadData->BackupLimit,
  1829. CookedReadData->CurrentPosition,
  1830. CookedReadData->ScreenInfo->ScreenBufferSize.X
  1831. -CookedReadData->OriginalCursorPosition.X)) {
  1832. CursorPosition.X++;
  1833. }
  1834. Status = AdjustCursorPosition(CookedReadData->ScreenInfo,
  1835. CursorPosition,
  1836. TRUE,
  1837. NULL);
  1838. UserAssert(NT_SUCCESS(Status));
  1839. }
  1840. }
  1841. NTSTATUS
  1842. RetrieveNthCommand(
  1843. IN PCOMMAND_HISTORY CommandHistory,
  1844. IN SHORT Index, // index, not command number
  1845. IN PWCHAR Buffer,
  1846. IN ULONG BufferSize,
  1847. OUT PULONG CommandSize)
  1848. {
  1849. PCOMMAND CommandRecord;
  1850. UserAssert(Index < CommandHistory->NumberOfCommands);
  1851. CommandHistory->LastDisplayed = Index;
  1852. CommandRecord = CommandHistory->Commands[Index];
  1853. if (CommandRecord->CommandLength > (USHORT)BufferSize) {
  1854. *CommandSize = (USHORT)BufferSize; // room for CRLF?
  1855. } else {
  1856. *CommandSize = CommandRecord->CommandLength;
  1857. }
  1858. RtlCopyMemory(Buffer,CommandRecord->Command,*CommandSize);
  1859. return STATUS_SUCCESS;
  1860. }
  1861. VOID
  1862. SetCurrentCommandLine(
  1863. IN PCOOKED_READ_DATA CookedReadData,
  1864. IN SHORT Index // index, not command number
  1865. )
  1866. /*++
  1867. This routine copies the commandline specified by Index
  1868. into the cooked read buffer
  1869. --*/
  1870. {
  1871. DWORD CharsToWrite;
  1872. NTSTATUS Status;
  1873. SHORT ScrollY=0;
  1874. DeleteCommandLine(CookedReadData,
  1875. TRUE);
  1876. Status = RetrieveNthCommand(CookedReadData->CommandHistory,
  1877. Index,
  1878. CookedReadData->BackupLimit,
  1879. CookedReadData->BufferSize,
  1880. &CookedReadData->BytesRead);
  1881. UserAssert(NT_SUCCESS(Status));
  1882. UserAssert(CookedReadData->BackupLimit == CookedReadData->BufPtr);
  1883. if (CookedReadData->Echo) {
  1884. Status = WriteCharsFromInput(CookedReadData->ScreenInfo,
  1885. CookedReadData->BackupLimit,
  1886. CookedReadData->BufPtr,
  1887. CookedReadData->BufPtr,
  1888. &CookedReadData->BytesRead,
  1889. (PLONG)&CookedReadData->NumberOfVisibleChars,
  1890. CookedReadData->OriginalCursorPosition.X,
  1891. WC_DESTRUCTIVE_BACKSPACE | WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
  1892. &ScrollY);
  1893. UserAssert(NT_SUCCESS(Status));
  1894. CookedReadData->OriginalCursorPosition.Y += ScrollY;
  1895. }
  1896. CharsToWrite = CookedReadData->BytesRead/sizeof(WCHAR);
  1897. CookedReadData->CurrentPosition = CharsToWrite;
  1898. CookedReadData->BufPtr = CookedReadData->BackupLimit + CharsToWrite;
  1899. }
  1900. BOOL
  1901. IsCommandLinePopupKey(
  1902. IN OUT PKEY_EVENT_RECORD KeyEvent
  1903. )
  1904. {
  1905. if (!(KeyEvent->dwControlKeyState &
  1906. (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED |
  1907. RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED))) {
  1908. switch (KeyEvent->wVirtualKeyCode) {
  1909. case VK_ESCAPE:
  1910. case VK_PRIOR:
  1911. case VK_NEXT:
  1912. case VK_END:
  1913. case VK_HOME:
  1914. case VK_LEFT:
  1915. case VK_UP:
  1916. case VK_RIGHT:
  1917. case VK_DOWN:
  1918. case VK_F9:
  1919. return TRUE;
  1920. default:
  1921. break;
  1922. }
  1923. }
  1924. //
  1925. // Extended key handling
  1926. //
  1927. if (gExtendedEditKey && ParseEditKeyInfo(KeyEvent)) {
  1928. return KeyEvent->uChar.UnicodeChar == 0;
  1929. }
  1930. return FALSE;
  1931. }
  1932. BOOL
  1933. IsCommandLineEditingKey(
  1934. IN PKEY_EVENT_RECORD KeyEvent
  1935. )
  1936. {
  1937. if (!(KeyEvent->dwControlKeyState &
  1938. (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED |
  1939. RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED))) {
  1940. switch (KeyEvent->wVirtualKeyCode) {
  1941. case VK_ESCAPE:
  1942. case VK_PRIOR:
  1943. case VK_NEXT:
  1944. case VK_END:
  1945. case VK_HOME:
  1946. case VK_LEFT:
  1947. case VK_UP:
  1948. case VK_RIGHT:
  1949. case VK_DOWN:
  1950. case VK_INSERT:
  1951. case VK_DELETE:
  1952. case VK_F1:
  1953. case VK_F2:
  1954. case VK_F3:
  1955. case VK_F4:
  1956. case VK_F5:
  1957. case VK_F6:
  1958. case VK_F7:
  1959. case VK_F8:
  1960. case VK_F9:
  1961. return TRUE;
  1962. default:
  1963. break;
  1964. }
  1965. }
  1966. if ((KeyEvent->dwControlKeyState &
  1967. (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED))) {
  1968. switch (KeyEvent->wVirtualKeyCode) {
  1969. case VK_END:
  1970. case VK_HOME:
  1971. case VK_LEFT:
  1972. case VK_RIGHT:
  1973. return TRUE;
  1974. default:
  1975. break;
  1976. }
  1977. }
  1978. //
  1979. // Extended edit key handling
  1980. //
  1981. if (gExtendedEditKey && ParseEditKeyInfo(KeyEvent)) {
  1982. //
  1983. // If wUnicodeChar is specified in KeySubst,
  1984. // the key should be handled as a normal key.
  1985. // Basically this is for VK_BACK keys.
  1986. //
  1987. return KeyEvent->uChar.UnicodeChar == 0;
  1988. }
  1989. if ((KeyEvent->dwControlKeyState &
  1990. (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED))) {
  1991. switch (KeyEvent->wVirtualKeyCode) {
  1992. case VK_F7:
  1993. case VK_F10:
  1994. return TRUE;
  1995. default:
  1996. break;
  1997. }
  1998. }
  1999. return FALSE;
  2000. }
  2001. NTSTATUS
  2002. ProcessCommandListInput(
  2003. IN PVOID CookedReadDataPtr,
  2004. IN PCSR_API_MSG WaitReplyMessage,
  2005. IN PCSR_THREAD WaitingThread,
  2006. IN BOOLEAN WaitRoutine
  2007. )
  2008. /*++
  2009. This routine handles the command list popup. It returns
  2010. when we're out of input or the user has selected a command line.
  2011. Return Value:
  2012. CONSOLE_STATUS_WAIT - we ran out of input, so
  2013. a wait block was created
  2014. CONSOLE_STATUS_READ_COMPLETE - user hit return
  2015. --*/
  2016. {
  2017. NTSTATUS Status;
  2018. PCLE_POPUP Popup;
  2019. PCOMMAND_HISTORY CommandHistory;
  2020. PCOOKED_READ_DATA CookedReadData=(PCOOKED_READ_DATA)CookedReadDataPtr;
  2021. WCHAR Char;
  2022. BOOLEAN CommandLinePopupKeys = FALSE;
  2023. PCONSOLE_READCONSOLE_MSG a;
  2024. PHANDLE_DATA HandleData;
  2025. SHORT Index;
  2026. CommandHistory = CookedReadData->CommandHistory;
  2027. Popup = CONTAINING_RECORD( CommandHistory->PopupList.Flink, CLE_POPUP, ListLink );
  2028. Status = DereferenceIoHandleNoCheck(CookedReadData->ProcessData,
  2029. CookedReadData->HandleIndex,
  2030. &HandleData
  2031. );
  2032. UserAssert(NT_SUCCESS(Status));
  2033. while (TRUE) {
  2034. Status = GetChar(CookedReadData->InputInfo,
  2035. &Char,
  2036. TRUE,
  2037. CookedReadData->Console,
  2038. HandleData,
  2039. WaitReplyMessage,
  2040. CookedReadWaitRoutine,
  2041. CookedReadData,
  2042. sizeof(*CookedReadData),
  2043. WaitRoutine,
  2044. NULL,
  2045. &CommandLinePopupKeys,
  2046. NULL,
  2047. NULL
  2048. );
  2049. if (!NT_SUCCESS(Status)) {
  2050. if (Status != CONSOLE_STATUS_WAIT) {
  2051. CookedReadData->BytesRead = 0;
  2052. }
  2053. return Status;
  2054. }
  2055. if (CommandLinePopupKeys) {
  2056. switch (Char) {
  2057. case VK_F9:
  2058. //
  2059. // prompt the user to enter the desired command number.
  2060. // copy that command to the command line.
  2061. //
  2062. {
  2063. COORD PopupSize;
  2064. if (CookedReadData->CommandHistory &&
  2065. CookedReadData->ScreenInfo->ScreenBufferSize.X >= MINIMUM_COMMAND_PROMPT_SIZE+2) { // 2 is for border
  2066. PopupSize.X = COMMAND_NUMBER_PROMPT_LENGTH+COMMAND_NUMBER_LENGTH;
  2067. PopupSize.Y = 1;
  2068. Status = BeginPopup(CookedReadData->ScreenInfo,
  2069. CookedReadData->CommandHistory,
  2070. PopupSize
  2071. );
  2072. if (NT_SUCCESS(Status)) {
  2073. // CommandNumberPopup does EndPopup call
  2074. return CommandNumberPopup(CookedReadData,
  2075. WaitReplyMessage,
  2076. WaitingThread,
  2077. WaitRoutine
  2078. );
  2079. }
  2080. }
  2081. }
  2082. break;
  2083. case VK_ESCAPE:
  2084. EndPopup(CookedReadData->ScreenInfo,CommandHistory);
  2085. HandleData->InputReadData->ReadCount += 1;
  2086. return CONSOLE_STATUS_WAIT_NO_BLOCK;
  2087. case VK_UP:
  2088. UpdateCommandListPopup(-1,
  2089. &Popup->CurrentCommand,
  2090. CommandHistory,
  2091. Popup,
  2092. CookedReadData->ScreenInfo, 0);
  2093. break;
  2094. case VK_DOWN:
  2095. UpdateCommandListPopup(1,
  2096. &Popup->CurrentCommand,
  2097. CommandHistory,
  2098. Popup,
  2099. CookedReadData->ScreenInfo, 0);
  2100. break;
  2101. case VK_END:
  2102. /*
  2103. * Move waaay forward, UpdateCommandListPopup() can handle it.
  2104. */
  2105. UpdateCommandListPopup((SHORT)(CommandHistory->NumberOfCommands),
  2106. &Popup->CurrentCommand,
  2107. CommandHistory,
  2108. Popup,
  2109. CookedReadData->ScreenInfo, 0);
  2110. break;
  2111. case VK_HOME:
  2112. /*
  2113. * Move waaay back, UpdateCommandListPopup() can handle it.
  2114. */
  2115. UpdateCommandListPopup((SHORT)-(CommandHistory->NumberOfCommands),
  2116. &Popup->CurrentCommand,
  2117. CommandHistory,
  2118. Popup,
  2119. CookedReadData->ScreenInfo, 0);
  2120. break;
  2121. case VK_PRIOR:
  2122. UpdateCommandListPopup((SHORT)-POPUP_SIZE_Y(Popup),
  2123. &Popup->CurrentCommand,
  2124. CommandHistory,
  2125. Popup,
  2126. CookedReadData->ScreenInfo, 0);
  2127. break;
  2128. case VK_NEXT:
  2129. UpdateCommandListPopup(POPUP_SIZE_Y(Popup),
  2130. &Popup->CurrentCommand,
  2131. CommandHistory,
  2132. Popup,
  2133. CookedReadData->ScreenInfo, 0);
  2134. break;
  2135. case VK_LEFT:
  2136. case VK_RIGHT:
  2137. Index = Popup->CurrentCommand;
  2138. EndPopup(CookedReadData->ScreenInfo,CommandHistory);
  2139. SetCurrentCommandLine(CookedReadData,Index);
  2140. HandleData->InputReadData->ReadCount += 1;
  2141. return CONSOLE_STATUS_WAIT_NO_BLOCK;
  2142. default:
  2143. break;
  2144. }
  2145. } else if (Char == UNICODE_CARRIAGERETURN) {
  2146. ULONG i,lStringLength;
  2147. DWORD LineCount=1;
  2148. Index = Popup->CurrentCommand;
  2149. EndPopup(CookedReadData->ScreenInfo,CommandHistory);
  2150. SetCurrentCommandLine(CookedReadData,Index);
  2151. lStringLength = CookedReadData->BytesRead;
  2152. ProcessCookedReadInput(CookedReadData,
  2153. UNICODE_CARRIAGERETURN,
  2154. 0,
  2155. &Status);
  2156. //
  2157. // complete read
  2158. //
  2159. if (CookedReadData->Echo) {
  2160. //
  2161. // check for alias
  2162. //
  2163. i = CookedReadData->BufferSize;
  2164. if (NT_SUCCESS(MatchandCopyAlias(CookedReadData->Console,
  2165. CookedReadData->BackupLimit,
  2166. (USHORT)lStringLength,
  2167. CookedReadData->BackupLimit,
  2168. (PUSHORT)&i,
  2169. CookedReadData->ExeName,
  2170. CookedReadData->ExeNameLength,
  2171. &LineCount
  2172. ))) {
  2173. CookedReadData->BytesRead = i;
  2174. }
  2175. CloseOutputHandle(CONSOLE_FROMTHREADPERPROCESSDATA(WaitingThread),
  2176. CookedReadData->Console,
  2177. &CookedReadData->TempHandle,
  2178. NULL,
  2179. FALSE
  2180. );
  2181. }
  2182. WaitReplyMessage->ReturnValue = STATUS_SUCCESS;
  2183. a = (PCONSOLE_READCONSOLE_MSG)&WaitReplyMessage->u.ApiMessageData;
  2184. if (CookedReadData->BytesRead > CookedReadData->UserBufferSize || LineCount > 1) {
  2185. if (LineCount > 1) {
  2186. PWSTR Tmp;
  2187. HandleData->InputReadData->InputHandleFlags |= HANDLE_MULTI_LINE_INPUT;
  2188. for (Tmp=CookedReadData->BackupLimit;*Tmp!=UNICODE_LINEFEED;Tmp++)
  2189. UserAssert(Tmp<(CookedReadData->BackupLimit+CookedReadData->BytesRead));
  2190. a->NumBytes = (ULONG)(Tmp-CookedReadData->BackupLimit+1)*sizeof(*Tmp);
  2191. } else {
  2192. a->NumBytes = CookedReadData->UserBufferSize;
  2193. }
  2194. HandleData->InputReadData->InputHandleFlags |= HANDLE_INPUT_PENDING;
  2195. HandleData->InputReadData->BufPtr = CookedReadData->BackupLimit;
  2196. HandleData->InputReadData->BytesAvailable = CookedReadData->BytesRead - a->NumBytes;
  2197. HandleData->InputReadData->CurrentBufPtr=(PWCHAR)((PBYTE)CookedReadData->BackupLimit+a->NumBytes);
  2198. RtlCopyMemory(CookedReadData->UserBuffer,CookedReadData->BackupLimit,a->NumBytes);
  2199. } else {
  2200. a->NumBytes = CookedReadData->BytesRead;
  2201. RtlCopyMemory(CookedReadData->UserBuffer,CookedReadData->BackupLimit,a->NumBytes);
  2202. }
  2203. if (!a->Unicode) {
  2204. //
  2205. // if ansi, translate string.
  2206. //
  2207. PCHAR TransBuffer;
  2208. TransBuffer = ConsoleHeapAlloc(TMP_TAG, CHAR_COUNT(a->NumBytes));
  2209. if (TransBuffer == NULL) {
  2210. return STATUS_NO_MEMORY;
  2211. }
  2212. a->NumBytes = (ULONG)ConvertToOem(CookedReadData->Console->CP,
  2213. CookedReadData->UserBuffer,
  2214. a->NumBytes / sizeof(WCHAR),
  2215. TransBuffer,
  2216. CHAR_COUNT(a->NumBytes)
  2217. );
  2218. RtlCopyMemory(CookedReadData->UserBuffer,TransBuffer,a->NumBytes);
  2219. ConsoleHeapFree(TransBuffer);
  2220. }
  2221. return CONSOLE_STATUS_READ_COMPLETE;
  2222. } else {
  2223. Index = FindMatchingCommand(CookedReadData->CommandHistory,
  2224. &Char, 1 * sizeof(WCHAR),
  2225. Popup->CurrentCommand, FMCFL_JUST_LOOKING);
  2226. if (Index != -1) {
  2227. UpdateCommandListPopup(
  2228. (SHORT)(Index - Popup->CurrentCommand),
  2229. &Popup->CurrentCommand,
  2230. CommandHistory,
  2231. Popup,
  2232. CookedReadData->ScreenInfo, UCLP_WRAP);
  2233. }
  2234. }
  2235. }
  2236. }
  2237. NTSTATUS
  2238. ProcessCopyFromCharInput(
  2239. IN PVOID CookedReadDataPtr,
  2240. IN PCSR_API_MSG WaitReplyMessage,
  2241. IN PCSR_THREAD WaitingThread,
  2242. IN BOOLEAN WaitRoutine
  2243. )
  2244. /*++
  2245. This routine handles the delete from cursor to char char popup. It returns
  2246. when we're out of input or the user has entered a char.
  2247. Return Value:
  2248. CONSOLE_STATUS_WAIT - we ran out of input, so
  2249. a wait block was created
  2250. CONSOLE_STATUS_READ_COMPLETE - user hit return
  2251. --*/
  2252. {
  2253. NTSTATUS Status;
  2254. PCOOKED_READ_DATA CookedReadData=(PCOOKED_READ_DATA)CookedReadDataPtr;
  2255. WCHAR Char;
  2256. PHANDLE_DATA HandleData;
  2257. int i; // char index (not byte)
  2258. UNREFERENCED_PARAMETER(WaitingThread);
  2259. Status = DereferenceIoHandleNoCheck(CookedReadData->ProcessData,
  2260. CookedReadData->HandleIndex,
  2261. &HandleData);
  2262. if (!NT_SUCCESS(Status)) {
  2263. return Status;
  2264. }
  2265. while (TRUE) {
  2266. Status = GetChar(CookedReadData->InputInfo,
  2267. &Char,
  2268. TRUE,
  2269. CookedReadData->Console,
  2270. HandleData,
  2271. WaitReplyMessage,
  2272. CookedReadWaitRoutine,
  2273. CookedReadData,
  2274. sizeof(*CookedReadData),
  2275. WaitRoutine,
  2276. NULL,
  2277. NULL,
  2278. NULL,
  2279. NULL
  2280. );
  2281. if (!NT_SUCCESS(Status)) {
  2282. if (Status != CONSOLE_STATUS_WAIT) {
  2283. CookedReadData->BytesRead = 0;
  2284. }
  2285. return Status;
  2286. }
  2287. EndPopup(CookedReadData->ScreenInfo,CookedReadData->CommandHistory);
  2288. //
  2289. // delete from cursor up to specified char
  2290. //
  2291. for (i=CookedReadData->CurrentPosition+1;
  2292. i<(int)(CookedReadData->BytesRead/sizeof(WCHAR));
  2293. i++) {
  2294. if (CookedReadData->BackupLimit[i] == Char) {
  2295. break;
  2296. }
  2297. }
  2298. if (i!=(int)(CookedReadData->BytesRead/sizeof(WCHAR)+1)) {
  2299. COORD CursorPosition;
  2300. //
  2301. // save cursor position
  2302. //
  2303. CursorPosition = CookedReadData->ScreenInfo->BufferInfo.TextInfo.CursorPosition;
  2304. //
  2305. // deletecommandline
  2306. //
  2307. DeleteCommandLine(CookedReadData,
  2308. FALSE);
  2309. //
  2310. // delete chars
  2311. //
  2312. RtlCopyMemory(&CookedReadData->BackupLimit[CookedReadData->CurrentPosition],
  2313. &CookedReadData->BackupLimit[i],
  2314. CookedReadData->BytesRead-(i*sizeof(WCHAR))
  2315. );
  2316. CookedReadData->BytesRead -= (i-CookedReadData->CurrentPosition)*sizeof(WCHAR);
  2317. //
  2318. // write commandline
  2319. //
  2320. if (CookedReadData->Echo) {
  2321. Status = WriteCharsFromInput(CookedReadData->ScreenInfo,
  2322. CookedReadData->BackupLimit,
  2323. CookedReadData->BackupLimit,
  2324. CookedReadData->BackupLimit,
  2325. &CookedReadData->BytesRead,
  2326. (PLONG)&CookedReadData->NumberOfVisibleChars,
  2327. CookedReadData->OriginalCursorPosition.X,
  2328. WC_DESTRUCTIVE_BACKSPACE |
  2329. WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
  2330. NULL
  2331. );
  2332. UserAssert(NT_SUCCESS(Status));
  2333. }
  2334. //
  2335. // restore cursor position
  2336. //
  2337. Status = SetCursorPosition(CookedReadData->ScreenInfo,
  2338. CursorPosition,
  2339. TRUE);
  2340. UserAssert(NT_SUCCESS(Status));
  2341. }
  2342. HandleData->InputReadData->ReadCount += 1;
  2343. return CONSOLE_STATUS_WAIT_NO_BLOCK;
  2344. }
  2345. }
  2346. NTSTATUS
  2347. ProcessCopyToCharInput(
  2348. IN PVOID CookedReadDataPtr,
  2349. IN PCSR_API_MSG WaitReplyMessage,
  2350. IN PCSR_THREAD WaitingThread,
  2351. IN BOOLEAN WaitRoutine
  2352. )
  2353. /*++
  2354. This routine handles the delete char popup. It returns
  2355. when we're out of input or the user has entered a char.
  2356. Return Value:
  2357. CONSOLE_STATUS_WAIT - we ran out of input, so
  2358. a wait block was created
  2359. CONSOLE_STATUS_READ_COMPLETE - user hit return
  2360. --*/
  2361. {
  2362. NTSTATUS Status;
  2363. PCOOKED_READ_DATA CookedReadData=(PCOOKED_READ_DATA)CookedReadDataPtr;
  2364. WCHAR Char;
  2365. PCOMMAND LastCommand;
  2366. DWORD NumSpaces;
  2367. SHORT ScrollY=0;
  2368. PHANDLE_DATA HandleData;
  2369. Status = DereferenceIoHandleNoCheck(CookedReadData->ProcessData,
  2370. CookedReadData->HandleIndex,
  2371. &HandleData);
  2372. if (!NT_SUCCESS(Status)) {
  2373. return Status;
  2374. }
  2375. while (TRUE) {
  2376. Status = GetChar(CookedReadData->InputInfo,
  2377. &Char,
  2378. TRUE,
  2379. CookedReadData->Console,
  2380. HandleData,
  2381. WaitReplyMessage,
  2382. CookedReadWaitRoutine,
  2383. CookedReadData,
  2384. sizeof(*CookedReadData),
  2385. WaitRoutine,
  2386. NULL,
  2387. NULL,
  2388. NULL,
  2389. NULL
  2390. );
  2391. if (!NT_SUCCESS(Status)) {
  2392. if (Status != CONSOLE_STATUS_WAIT) {
  2393. CookedReadData->BytesRead = 0;
  2394. }
  2395. return Status;
  2396. }
  2397. EndPopup(CookedReadData->ScreenInfo,CookedReadData->CommandHistory);
  2398. //
  2399. // copy up to specified char
  2400. //
  2401. LastCommand = GetLastCommand(CookedReadData->CommandHistory);
  2402. if (LastCommand) {
  2403. int i,j;
  2404. //
  2405. // find specified char in last command
  2406. //
  2407. for (i=CookedReadData->CurrentPosition+1;i<(int)(LastCommand->CommandLength/sizeof(WCHAR));i++) {
  2408. if (LastCommand->Command[i] == Char)
  2409. break;
  2410. }
  2411. //
  2412. // if we found it, copy up to it
  2413. //
  2414. if (i<(int)(LastCommand->CommandLength/sizeof(WCHAR)) && (USHORT)(LastCommand->CommandLength/sizeof(WCHAR)) > (USHORT)CookedReadData->CurrentPosition) {
  2415. j=i-CookedReadData->CurrentPosition;
  2416. UserAssert(j > 0);
  2417. RtlCopyMemory(CookedReadData->BufPtr,
  2418. &LastCommand->Command[CookedReadData->CurrentPosition],
  2419. j*sizeof(WCHAR));
  2420. CookedReadData->CurrentPosition += j;
  2421. j*=sizeof(WCHAR);
  2422. CookedReadData->BytesRead = max(CookedReadData->BytesRead,
  2423. CookedReadData->CurrentPosition * sizeof(WCHAR));
  2424. if (CookedReadData->Echo) {
  2425. Status = WriteCharsFromInput(CookedReadData->ScreenInfo,
  2426. CookedReadData->BackupLimit,
  2427. CookedReadData->BufPtr,
  2428. CookedReadData->BufPtr,
  2429. (PDWORD) &j,
  2430. (PLONG)&NumSpaces,
  2431. CookedReadData->OriginalCursorPosition.X,
  2432. WC_DESTRUCTIVE_BACKSPACE |
  2433. WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
  2434. &ScrollY);
  2435. UserAssert(NT_SUCCESS(Status));
  2436. CookedReadData->OriginalCursorPosition.Y += ScrollY;
  2437. CookedReadData->NumberOfVisibleChars += NumSpaces;
  2438. }
  2439. CookedReadData->BufPtr+=j/sizeof(WCHAR);
  2440. }
  2441. }
  2442. HandleData->InputReadData->ReadCount += 1;
  2443. return CONSOLE_STATUS_WAIT_NO_BLOCK;
  2444. }
  2445. UNREFERENCED_PARAMETER(WaitingThread);
  2446. }
  2447. NTSTATUS
  2448. ProcessCommandNumberInput(
  2449. IN PVOID CookedReadDataPtr,
  2450. IN PCSR_API_MSG WaitReplyMessage,
  2451. IN PCSR_THREAD WaitingThread,
  2452. IN BOOLEAN WaitRoutine
  2453. )
  2454. /*++
  2455. This routine handles the delete char popup. It returns
  2456. when we're out of input or the user has entered a char.
  2457. Return Value:
  2458. CONSOLE_STATUS_WAIT - we ran out of input, so
  2459. a wait block was created
  2460. CONSOLE_STATUS_READ_COMPLETE - user hit return
  2461. --*/
  2462. {
  2463. NTSTATUS Status;
  2464. PCLE_POPUP Popup;
  2465. PCOMMAND_HISTORY CommandHistory;
  2466. PCOOKED_READ_DATA CookedReadData=(PCOOKED_READ_DATA)CookedReadDataPtr;
  2467. WCHAR Char;
  2468. DWORD NumSpaces;
  2469. BOOLEAN CommandLinePopupKeys;
  2470. SHORT CommandNumber;
  2471. PHANDLE_DATA HandleData;
  2472. CommandHistory = CookedReadData->CommandHistory;
  2473. Popup = CONTAINING_RECORD( CommandHistory->PopupList.Flink, CLE_POPUP, ListLink );
  2474. Status = DereferenceIoHandleNoCheck(CookedReadData->ProcessData,
  2475. CookedReadData->HandleIndex,
  2476. &HandleData);
  2477. UserAssert(NT_SUCCESS(Status));
  2478. while (TRUE) {
  2479. Status = GetChar(CookedReadData->InputInfo,
  2480. &Char,
  2481. TRUE,
  2482. CookedReadData->Console,
  2483. HandleData,
  2484. WaitReplyMessage,
  2485. CookedReadWaitRoutine,
  2486. CookedReadData,
  2487. sizeof(*CookedReadData),
  2488. WaitRoutine,
  2489. NULL,
  2490. &CommandLinePopupKeys,
  2491. NULL,
  2492. NULL
  2493. );
  2494. if (!NT_SUCCESS(Status)) {
  2495. if (Status != CONSOLE_STATUS_WAIT) {
  2496. CookedReadData->BytesRead = 0;
  2497. }
  2498. return Status;
  2499. }
  2500. if (Char >= (WCHAR)0x30 && Char <= (WCHAR)0x39) {
  2501. if (Popup->NumberRead < 5) {
  2502. DWORD CharsToWrite;
  2503. WORD RealAttributes;
  2504. RealAttributes = CookedReadData->ScreenInfo->Attributes;
  2505. CookedReadData->ScreenInfo->Attributes = Popup->Attributes;
  2506. CharsToWrite = sizeof(WCHAR);
  2507. Status = WriteCharsFromInput(CookedReadData->ScreenInfo,
  2508. Popup->NumberBuffer,
  2509. &Popup->NumberBuffer[Popup->NumberRead],
  2510. &Char,
  2511. &CharsToWrite,
  2512. (PLONG)&NumSpaces,
  2513. CookedReadData->OriginalCursorPosition.X,
  2514. WC_DESTRUCTIVE_BACKSPACE |
  2515. WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
  2516. NULL);
  2517. UserAssert(NT_SUCCESS(Status));
  2518. CookedReadData->ScreenInfo->Attributes = RealAttributes;
  2519. Popup->NumberBuffer[Popup->NumberRead] = Char;
  2520. Popup->NumberRead += 1;
  2521. }
  2522. } else if (Char == UNICODE_BACKSPACE) {
  2523. if (Popup->NumberRead > 0) {
  2524. DWORD CharsToWrite;
  2525. WORD RealAttributes;
  2526. RealAttributes = CookedReadData->ScreenInfo->Attributes;
  2527. CookedReadData->ScreenInfo->Attributes = Popup->Attributes;
  2528. CharsToWrite = sizeof(WCHAR);
  2529. Status = WriteCharsFromInput(CookedReadData->ScreenInfo,
  2530. Popup->NumberBuffer,
  2531. &Popup->NumberBuffer[Popup->NumberRead],
  2532. &Char,
  2533. &CharsToWrite,
  2534. (PLONG)&NumSpaces,
  2535. CookedReadData->OriginalCursorPosition.X,
  2536. WC_DESTRUCTIVE_BACKSPACE |
  2537. WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
  2538. NULL
  2539. );
  2540. UserAssert(NT_SUCCESS(Status));
  2541. CookedReadData->ScreenInfo->Attributes = RealAttributes;
  2542. Popup->NumberBuffer[Popup->NumberRead] = (WCHAR)' ';
  2543. Popup->NumberRead -= 1;
  2544. }
  2545. } else if (Char == (WCHAR)VK_ESCAPE) {
  2546. EndPopup(CookedReadData->ScreenInfo,CookedReadData->CommandHistory);
  2547. if (!CLE_NO_POPUPS(CommandHistory)) {
  2548. EndPopup(CookedReadData->ScreenInfo,CookedReadData->CommandHistory);
  2549. }
  2550. DeleteCommandLine(CookedReadData,
  2551. TRUE);
  2552. } else if (Char == UNICODE_CARRIAGERETURN) {
  2553. CHAR NumberBuffer[6];
  2554. int i;
  2555. for (i=0;i<Popup->NumberRead;i++) {
  2556. NumberBuffer[i] = (CHAR)Popup->NumberBuffer[i];
  2557. }
  2558. NumberBuffer[i] = 0;
  2559. CommandNumber = (SHORT)atoi(NumberBuffer);
  2560. if ((WORD)CommandNumber >= (WORD)CookedReadData->CommandHistory->NumberOfCommands) {
  2561. CommandNumber = (SHORT)(CookedReadData->CommandHistory->NumberOfCommands-1);
  2562. }
  2563. EndPopup(CookedReadData->ScreenInfo,CookedReadData->CommandHistory);
  2564. if (!CLE_NO_POPUPS(CommandHistory)) {
  2565. EndPopup(CookedReadData->ScreenInfo,CookedReadData->CommandHistory);
  2566. }
  2567. SetCurrentCommandLine(CookedReadData,COMMAND_NUM_TO_INDEX(CommandNumber,CookedReadData->CommandHistory));
  2568. }
  2569. HandleData->InputReadData->ReadCount += 1;
  2570. return CONSOLE_STATUS_WAIT_NO_BLOCK;
  2571. }
  2572. UNREFERENCED_PARAMETER(WaitingThread);
  2573. }
  2574. NTSTATUS
  2575. CommandListPopup(
  2576. IN PCOOKED_READ_DATA CookedReadData,
  2577. IN PCSR_API_MSG WaitReplyMessage,
  2578. IN PCSR_THREAD WaitingThread,
  2579. IN BOOLEAN WaitRoutine
  2580. )
  2581. /*++
  2582. This routine handles the command list popup. It puts up the
  2583. popup, then calls ProcessCommandListInput to get and process
  2584. input.
  2585. Return Value:
  2586. CONSOLE_STATUS_WAIT - we ran out of input, so
  2587. a wait block was created
  2588. STATUS_SUCCESS - read was fully completed (user hit return)
  2589. --*/
  2590. {
  2591. SHORT CurrentCommand;
  2592. PCLE_POPUP Popup;
  2593. PCOMMAND_HISTORY CommandHistory;
  2594. CommandHistory = CookedReadData->CommandHistory;
  2595. Popup = CONTAINING_RECORD( CommandHistory->PopupList.Flink, CLE_POPUP, ListLink );
  2596. CurrentCommand = COMMAND_INDEX_TO_NUM(CommandHistory->LastDisplayed,CommandHistory);
  2597. if (CurrentCommand < (SHORT)(CommandHistory->NumberOfCommands - POPUP_SIZE_Y(Popup))) {
  2598. Popup->BottomIndex = (SHORT)(max(CurrentCommand,POPUP_SIZE_Y(Popup)-1));
  2599. } else {
  2600. Popup->BottomIndex = (SHORT)(CommandHistory->NumberOfCommands-1);
  2601. }
  2602. Popup->CurrentCommand = CommandHistory->LastDisplayed;
  2603. DrawCommandListPopup(Popup,
  2604. CommandHistory->LastDisplayed,
  2605. CommandHistory,
  2606. CookedReadData->ScreenInfo);
  2607. Popup->PopupInputRoutine = ProcessCommandListInput;
  2608. return ProcessCommandListInput(CookedReadData,
  2609. WaitReplyMessage,
  2610. WaitingThread,
  2611. WaitRoutine);
  2612. }
  2613. VOID
  2614. DrawPromptPopup(
  2615. IN PCLE_POPUP Popup,
  2616. IN PSCREEN_INFORMATION ScreenInfo,
  2617. IN PWCHAR Prompt,
  2618. IN ULONG PromptLength // in chars
  2619. )
  2620. {
  2621. ULONG lStringLength;
  2622. COORD WriteCoord;
  2623. SHORT i;
  2624. //
  2625. // draw empty popup
  2626. //
  2627. WriteCoord.X = (SHORT)(Popup->Region.Left+1);
  2628. WriteCoord.Y = (SHORT)(Popup->Region.Top+1);
  2629. lStringLength = POPUP_SIZE_X(Popup);
  2630. for (i=0;i<POPUP_SIZE_Y(Popup);i++) {
  2631. FillOutput(ScreenInfo,
  2632. Popup->Attributes,
  2633. WriteCoord,
  2634. CONSOLE_ATTRIBUTE,
  2635. &lStringLength
  2636. );
  2637. FillOutput(ScreenInfo,
  2638. (WCHAR)' ',
  2639. WriteCoord,
  2640. CONSOLE_FALSE_UNICODE, // faster that real unicode
  2641. &lStringLength
  2642. );
  2643. WriteCoord.Y += 1;
  2644. }
  2645. WriteCoord.X = (SHORT)(Popup->Region.Left+1);
  2646. WriteCoord.Y = (SHORT)(Popup->Region.Top+1);
  2647. //
  2648. // write prompt to screen
  2649. //
  2650. lStringLength = PromptLength;
  2651. if (lStringLength > (ULONG)POPUP_SIZE_X(Popup))
  2652. lStringLength = (ULONG)(POPUP_SIZE_X(Popup));
  2653. WriteOutputString(ScreenInfo,
  2654. Prompt,
  2655. WriteCoord,
  2656. CONSOLE_REAL_UNICODE,
  2657. &lStringLength,
  2658. NULL
  2659. );
  2660. }
  2661. NTSTATUS
  2662. CopyFromCharPopup(
  2663. IN PCOOKED_READ_DATA CookedReadData,
  2664. IN PCSR_API_MSG WaitReplyMessage,
  2665. IN PCSR_THREAD WaitingThread,
  2666. IN BOOLEAN WaitRoutine
  2667. )
  2668. /*++
  2669. This routine handles the "delete up to this char" popup. It puts up the
  2670. popup, then calls ProcessCopyFromCharInput to get and process
  2671. input.
  2672. Return Value:
  2673. CONSOLE_STATUS_WAIT - we ran out of input, so
  2674. a wait block was created
  2675. STATUS_SUCCESS - read was fully completed (user hit return)
  2676. --*/
  2677. {
  2678. PCLE_POPUP Popup;
  2679. PCOMMAND_HISTORY CommandHistory;
  2680. WCHAR ItemString[70];
  2681. int ItemLength;
  2682. NTSTATUS Status;
  2683. LANGID LangId;
  2684. Status = GetConsoleLangId(CookedReadData->ScreenInfo->Console->OutputCP, &LangId);
  2685. if (NT_SUCCESS(Status)) {
  2686. ItemLength = LoadStringEx(ghInstance,msgCmdLineF4,ItemString,70,LangId);
  2687. }
  2688. if (!NT_SUCCESS(Status) || ItemLength == 0) {
  2689. ItemLength = LoadString(ghInstance,msgCmdLineF4,ItemString,70);
  2690. }
  2691. CommandHistory = CookedReadData->CommandHistory;
  2692. Popup = CONTAINING_RECORD( CommandHistory->PopupList.Flink, CLE_POPUP, ListLink );
  2693. DrawPromptPopup(Popup,
  2694. CookedReadData->ScreenInfo,
  2695. ItemString,
  2696. ItemLength
  2697. );
  2698. Popup->PopupInputRoutine = ProcessCopyFromCharInput;
  2699. return ProcessCopyFromCharInput(CookedReadData,
  2700. WaitReplyMessage,
  2701. WaitingThread,
  2702. WaitRoutine);
  2703. }
  2704. NTSTATUS
  2705. CopyToCharPopup(
  2706. IN PCOOKED_READ_DATA CookedReadData,
  2707. IN PCSR_API_MSG WaitReplyMessage,
  2708. IN PCSR_THREAD WaitingThread,
  2709. IN BOOLEAN WaitRoutine
  2710. )
  2711. /*++
  2712. This routine handles the "delete up to this char" popup. It puts up the
  2713. popup, then calls ProcessCopyToCharInput to get and process
  2714. input.
  2715. Return Value:
  2716. CONSOLE_STATUS_WAIT - we ran out of input, so
  2717. a wait block was created
  2718. STATUS_SUCCESS - read was fully completed (user hit return)
  2719. --*/
  2720. {
  2721. PCLE_POPUP Popup;
  2722. PCOMMAND_HISTORY CommandHistory;
  2723. WCHAR ItemString[70];
  2724. int ItemLength;
  2725. NTSTATUS Status;
  2726. LANGID LangId;
  2727. Status = GetConsoleLangId(CookedReadData->ScreenInfo->Console->OutputCP, &LangId);
  2728. if (NT_SUCCESS(Status)) {
  2729. ItemLength = LoadStringEx(ghInstance,msgCmdLineF2,ItemString,70,LangId);
  2730. }
  2731. if (!NT_SUCCESS(Status) || ItemLength == 0) {
  2732. ItemLength = LoadString(ghInstance,msgCmdLineF2,ItemString,70);
  2733. }
  2734. CommandHistory = CookedReadData->CommandHistory;
  2735. Popup = CONTAINING_RECORD( CommandHistory->PopupList.Flink, CLE_POPUP, ListLink );
  2736. DrawPromptPopup(Popup,
  2737. CookedReadData->ScreenInfo,
  2738. ItemString,
  2739. ItemLength
  2740. );
  2741. Popup->PopupInputRoutine = ProcessCopyToCharInput;
  2742. return ProcessCopyToCharInput(CookedReadData,
  2743. WaitReplyMessage,
  2744. WaitingThread,
  2745. WaitRoutine);
  2746. }
  2747. NTSTATUS
  2748. CommandNumberPopup(
  2749. IN PCOOKED_READ_DATA CookedReadData,
  2750. IN PCSR_API_MSG WaitReplyMessage,
  2751. IN PCSR_THREAD WaitingThread,
  2752. IN BOOLEAN WaitRoutine
  2753. )
  2754. /*++
  2755. This routine handles the "enter command number" popup. It puts up the
  2756. popup, then calls ProcessCommandNumberInput to get and process
  2757. input.
  2758. Return Value:
  2759. CONSOLE_STATUS_WAIT - we ran out of input, so
  2760. a wait block was created
  2761. STATUS_SUCCESS - read was fully completed (user hit return)
  2762. --*/
  2763. {
  2764. PCLE_POPUP Popup;
  2765. PCOMMAND_HISTORY CommandHistory;
  2766. COORD CursorPosition;
  2767. WCHAR ItemString[70];
  2768. int ItemLength;
  2769. NTSTATUS Status;
  2770. LANGID LangId;
  2771. CommandHistory = CookedReadData->CommandHistory;
  2772. Popup = CONTAINING_RECORD( CommandHistory->PopupList.Flink, CLE_POPUP, ListLink );
  2773. Status = GetConsoleLangId(CookedReadData->ScreenInfo->Console->OutputCP, &LangId);
  2774. if (NT_SUCCESS(Status)) {
  2775. ItemLength = LoadStringEx(ghInstance,msgCmdLineF9,ItemString,70,LangId);
  2776. }
  2777. if (!NT_SUCCESS(Status) || ItemLength == 0) {
  2778. ItemLength = LoadString(ghInstance,msgCmdLineF9,ItemString,70);
  2779. }
  2780. if (ItemLength > POPUP_SIZE_X(Popup) - COMMAND_NUMBER_LENGTH) {
  2781. ItemLength = POPUP_SIZE_X(Popup) - COMMAND_NUMBER_LENGTH;
  2782. }
  2783. DrawPromptPopup(Popup,
  2784. CookedReadData->ScreenInfo,
  2785. ItemString,
  2786. ItemLength
  2787. );
  2788. CursorPosition.X = (SHORT)(Popup->Region.Right - MINIMUM_COMMAND_PROMPT_SIZE);
  2789. CursorPosition.Y = (SHORT)(Popup->Region.Top+1);
  2790. SetCursorPosition(CookedReadData->ScreenInfo,
  2791. CursorPosition,
  2792. TRUE
  2793. );
  2794. Popup->NumberRead=0;
  2795. Popup->PopupInputRoutine = ProcessCommandNumberInput;
  2796. return ProcessCommandNumberInput(CookedReadData,
  2797. WaitReplyMessage,
  2798. WaitingThread,
  2799. WaitRoutine);
  2800. }
  2801. PCOMMAND
  2802. GetLastCommand(
  2803. IN PCOMMAND_HISTORY CommandHistory)
  2804. {
  2805. if (CommandHistory->NumberOfCommands == 0) {
  2806. return NULL;
  2807. } else {
  2808. return CommandHistory->Commands[CommandHistory->LastDisplayed];
  2809. }
  2810. }
  2811. VOID
  2812. EmptyCommandHistory(
  2813. IN PCOMMAND_HISTORY CommandHistory)
  2814. {
  2815. SHORT i;
  2816. if (CommandHistory == NULL) {
  2817. return;
  2818. }
  2819. for (i = 0; i < CommandHistory->NumberOfCommands; i++) {
  2820. ConsoleHeapFree(CommandHistory->Commands[i]);
  2821. }
  2822. CommandHistory->NumberOfCommands = 0;
  2823. CommandHistory->LastAdded = -1;
  2824. CommandHistory->LastDisplayed = -1;
  2825. CommandHistory->FirstCommand = 0;
  2826. CommandHistory->Flags = CLE_RESET;
  2827. }
  2828. BOOL
  2829. AtFirstCommand(
  2830. IN PCOMMAND_HISTORY CommandHistory)
  2831. {
  2832. SHORT i;
  2833. if (CommandHistory == NULL) {
  2834. return FALSE;
  2835. }
  2836. if (CommandHistory->Flags & CLE_RESET) {
  2837. return FALSE;
  2838. }
  2839. i = (SHORT)(CommandHistory->LastDisplayed - 1);
  2840. if (i == -1) {
  2841. i = (SHORT)(CommandHistory->NumberOfCommands - 1);
  2842. }
  2843. return (i == CommandHistory->LastAdded);
  2844. }
  2845. BOOL
  2846. AtLastCommand(
  2847. IN PCOMMAND_HISTORY CommandHistory)
  2848. {
  2849. if (CommandHistory == NULL) {
  2850. return FALSE;
  2851. } else {
  2852. return (CommandHistory->LastDisplayed == CommandHistory->LastAdded);
  2853. }
  2854. }
  2855. NTSTATUS
  2856. ProcessCommandLine(
  2857. IN PCOOKED_READ_DATA CookedReadData,
  2858. IN WCHAR Char,
  2859. IN DWORD KeyState,
  2860. IN PCSR_API_MSG WaitReplyMessage,
  2861. IN PCSR_THREAD WaitingThread,
  2862. IN BOOLEAN WaitRoutine
  2863. )
  2864. /*++
  2865. This routine process command line editing keys.
  2866. Return Value:
  2867. CONSOLE_STATUS_WAIT - CommandListPopup ran out of input
  2868. CONSOLE_STATUS_READ_COMPLETE - user hit <enter> in CommandListPopup
  2869. STATUS_SUCCESS - everything's cool
  2870. --*/
  2871. {
  2872. COORD CurrentPosition;
  2873. DWORD CharsToWrite;
  2874. NTSTATUS Status;
  2875. BOOL UpdateCursorPosition;
  2876. SHORT ScrollY=0;
  2877. BOOL fStartFromDelim;
  2878. UpdateCursorPosition = FALSE;
  2879. if (Char == VK_F7 &&
  2880. !(KeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED))) {
  2881. COORD PopupSize;
  2882. if (CookedReadData->CommandHistory &&
  2883. CookedReadData->CommandHistory->NumberOfCommands) {
  2884. PopupSize.X = 40;
  2885. PopupSize.Y = 10;
  2886. Status = BeginPopup(CookedReadData->ScreenInfo,
  2887. CookedReadData->CommandHistory,
  2888. PopupSize
  2889. );
  2890. if (NT_SUCCESS(Status)) {
  2891. // CommandListPopup does EndPopup call
  2892. return CommandListPopup(CookedReadData,
  2893. WaitReplyMessage,
  2894. WaitingThread,
  2895. WaitRoutine
  2896. );
  2897. }
  2898. }
  2899. } else {
  2900. switch (Char) {
  2901. case VK_ESCAPE:
  2902. DeleteCommandLine(CookedReadData,
  2903. TRUE);
  2904. break;
  2905. case VK_UP:
  2906. case VK_DOWN:
  2907. case VK_F5:
  2908. if (Char == VK_F5)
  2909. Char = VK_UP;
  2910. // for doskey compatibility, buffer isn't circular
  2911. if (Char==VK_UP && !AtFirstCommand(CookedReadData->CommandHistory) ||
  2912. Char==VK_DOWN && !AtLastCommand(CookedReadData->CommandHistory)) {
  2913. DeleteCommandLine(CookedReadData,
  2914. TRUE);
  2915. Status = RetrieveCommand(CookedReadData->CommandHistory,
  2916. Char,
  2917. CookedReadData->BackupLimit,
  2918. CookedReadData->BufferSize,
  2919. &CookedReadData->BytesRead);
  2920. UserAssert(CookedReadData->BackupLimit == CookedReadData->BufPtr);
  2921. if (CookedReadData->Echo) {
  2922. Status = WriteCharsFromInput(CookedReadData->ScreenInfo,
  2923. CookedReadData->BackupLimit,
  2924. CookedReadData->BufPtr,
  2925. CookedReadData->BufPtr,
  2926. &CookedReadData->BytesRead,
  2927. (PLONG)&CookedReadData->NumberOfVisibleChars,
  2928. CookedReadData->OriginalCursorPosition.X,
  2929. WC_DESTRUCTIVE_BACKSPACE |
  2930. WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
  2931. &ScrollY );
  2932. UserAssert(NT_SUCCESS(Status));
  2933. CookedReadData->OriginalCursorPosition.Y += ScrollY;
  2934. }
  2935. CharsToWrite = CookedReadData->BytesRead/sizeof(WCHAR);
  2936. CookedReadData->CurrentPosition = CharsToWrite;
  2937. CookedReadData->BufPtr = CookedReadData->BackupLimit + CharsToWrite;
  2938. }
  2939. break;
  2940. case VK_PRIOR:
  2941. case VK_NEXT:
  2942. if (CookedReadData->CommandHistory &&
  2943. CookedReadData->CommandHistory->NumberOfCommands) {
  2944. //
  2945. // display oldest or newest command
  2946. //
  2947. SHORT CommandNumber;
  2948. if (Char == VK_PRIOR) {
  2949. CommandNumber = 0;
  2950. } else {
  2951. CommandNumber = (SHORT)(CookedReadData->CommandHistory->NumberOfCommands-1);
  2952. }
  2953. DeleteCommandLine(CookedReadData,
  2954. TRUE);
  2955. Status = RetrieveNthCommand(CookedReadData->CommandHistory,
  2956. COMMAND_NUM_TO_INDEX(CommandNumber,CookedReadData->CommandHistory),
  2957. CookedReadData->BackupLimit,
  2958. CookedReadData->BufferSize,
  2959. &CookedReadData->BytesRead);
  2960. UserAssert(CookedReadData->BackupLimit == CookedReadData->BufPtr);
  2961. if (CookedReadData->Echo) {
  2962. Status = WriteCharsFromInput(CookedReadData->ScreenInfo,
  2963. CookedReadData->BackupLimit,
  2964. CookedReadData->BufPtr,
  2965. CookedReadData->BufPtr,
  2966. &CookedReadData->BytesRead,
  2967. (PLONG)&CookedReadData->NumberOfVisibleChars,
  2968. CookedReadData->OriginalCursorPosition.X,
  2969. WC_DESTRUCTIVE_BACKSPACE |
  2970. WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
  2971. &ScrollY
  2972. );
  2973. UserAssert(NT_SUCCESS(Status));
  2974. CookedReadData->OriginalCursorPosition.Y += ScrollY;
  2975. }
  2976. CharsToWrite = CookedReadData->BytesRead/sizeof(WCHAR);
  2977. CookedReadData->CurrentPosition = CharsToWrite;
  2978. CookedReadData->BufPtr = CookedReadData->BackupLimit + CharsToWrite;
  2979. }
  2980. break;
  2981. case VK_END:
  2982. if (KeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) {
  2983. DeleteCommandLine(CookedReadData,
  2984. FALSE);
  2985. CookedReadData->BytesRead = CookedReadData->CurrentPosition*sizeof(WCHAR);
  2986. if (CookedReadData->Echo) {
  2987. Status = WriteCharsFromInput(CookedReadData->ScreenInfo,
  2988. CookedReadData->BackupLimit,
  2989. CookedReadData->BackupLimit,
  2990. CookedReadData->BackupLimit,
  2991. &CookedReadData->BytesRead,
  2992. (PLONG)&CookedReadData->NumberOfVisibleChars,
  2993. CookedReadData->OriginalCursorPosition.X,
  2994. WC_DESTRUCTIVE_BACKSPACE |
  2995. WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
  2996. NULL
  2997. );
  2998. UserAssert(NT_SUCCESS(Status));
  2999. }
  3000. } else {
  3001. CookedReadData->CurrentPosition = CookedReadData->BytesRead/sizeof(WCHAR);
  3002. CookedReadData->BufPtr = CookedReadData->BackupLimit + CookedReadData->CurrentPosition;
  3003. CurrentPosition.X = (SHORT)(CookedReadData->OriginalCursorPosition.X + CookedReadData->NumberOfVisibleChars);
  3004. CurrentPosition.Y = CookedReadData->OriginalCursorPosition.Y;
  3005. #if defined(FE_SB)
  3006. if (CheckBisectProcessW(CookedReadData->ScreenInfo,
  3007. CookedReadData->ScreenInfo->Console->CP,
  3008. CookedReadData->BackupLimit,
  3009. CookedReadData->CurrentPosition,
  3010. CookedReadData->ScreenInfo->ScreenBufferSize.X-CookedReadData->OriginalCursorPosition.X,
  3011. CookedReadData->OriginalCursorPosition.X,
  3012. TRUE)) {
  3013. CurrentPosition.X++;
  3014. }
  3015. #endif
  3016. UpdateCursorPosition = TRUE;
  3017. }
  3018. break;
  3019. case VK_HOME:
  3020. if (KeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) {
  3021. DeleteCommandLine(CookedReadData,
  3022. FALSE);
  3023. CookedReadData->BytesRead -= CookedReadData->CurrentPosition*sizeof(WCHAR);
  3024. CookedReadData->CurrentPosition = 0;
  3025. RtlCopyMemory(CookedReadData->BackupLimit,
  3026. CookedReadData->BufPtr,
  3027. CookedReadData->BytesRead
  3028. );
  3029. CookedReadData->BufPtr = CookedReadData->BackupLimit;
  3030. if (CookedReadData->Echo) {
  3031. Status = WriteCharsFromInput(CookedReadData->ScreenInfo,
  3032. CookedReadData->BackupLimit,
  3033. CookedReadData->BackupLimit,
  3034. CookedReadData->BackupLimit,
  3035. &CookedReadData->BytesRead,
  3036. (PLONG)&CookedReadData->NumberOfVisibleChars,
  3037. CookedReadData->OriginalCursorPosition.X,
  3038. WC_DESTRUCTIVE_BACKSPACE |
  3039. WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
  3040. NULL
  3041. );
  3042. UserAssert(NT_SUCCESS(Status));
  3043. }
  3044. CurrentPosition = CookedReadData->OriginalCursorPosition;
  3045. UpdateCursorPosition = TRUE;
  3046. } else {
  3047. CookedReadData->CurrentPosition = 0;
  3048. CookedReadData->BufPtr = CookedReadData->BackupLimit;
  3049. CurrentPosition = CookedReadData->OriginalCursorPosition;
  3050. UpdateCursorPosition = TRUE;
  3051. }
  3052. break;
  3053. case VK_LEFT:
  3054. if (KeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) {
  3055. PWCHAR LastWord;
  3056. BOOL NonSpaceCharSeen=FALSE;
  3057. if (CookedReadData->BufPtr != CookedReadData->BackupLimit) {
  3058. if (!gExtendedEditKey) {
  3059. LastWord = CookedReadData->BufPtr-1;
  3060. while (LastWord != CookedReadData->BackupLimit) {
  3061. if (!IS_WORD_DELIM(*LastWord)) {
  3062. NonSpaceCharSeen=TRUE;
  3063. } else {
  3064. if (NonSpaceCharSeen) {
  3065. break;
  3066. }
  3067. }
  3068. LastWord--;
  3069. }
  3070. if (LastWord != CookedReadData->BackupLimit) {
  3071. CookedReadData->BufPtr = LastWord+1;
  3072. } else {
  3073. CookedReadData->BufPtr = LastWord;
  3074. }
  3075. } else {
  3076. /*
  3077. * A bit better word skipping.
  3078. */
  3079. LastWord = CookedReadData->BufPtr - 1;
  3080. if (LastWord != CookedReadData->BackupLimit) {
  3081. if (*LastWord == L' ') {
  3082. /*
  3083. * Skip spaces, until the non-space character is found.
  3084. */
  3085. while (--LastWord != CookedReadData->BackupLimit) {
  3086. UserAssert(LastWord > CookedReadData->BackupLimit);
  3087. if (*LastWord != L' ') {
  3088. break;
  3089. }
  3090. }
  3091. }
  3092. if (LastWord != CookedReadData->BackupLimit) {
  3093. if (IS_WORD_DELIM(*LastWord)) {
  3094. /*
  3095. * Skip WORD_DELIMs until space or non WORD_DELIM is found.
  3096. */
  3097. while (--LastWord != CookedReadData->BackupLimit) {
  3098. UserAssert(LastWord > CookedReadData->BackupLimit);
  3099. if (*LastWord == L' ' || !IS_WORD_DELIM(*LastWord)) {
  3100. break;
  3101. }
  3102. }
  3103. } else {
  3104. /*
  3105. * Skip the regular words
  3106. */
  3107. while (--LastWord != CookedReadData->BackupLimit) {
  3108. UserAssert(LastWord > CookedReadData->BackupLimit);
  3109. if (IS_WORD_DELIM(*LastWord)) {
  3110. break;
  3111. }
  3112. }
  3113. }
  3114. }
  3115. }
  3116. UserAssert(LastWord >= CookedReadData->BackupLimit);
  3117. if (LastWord != CookedReadData->BackupLimit) {
  3118. /*
  3119. * LastWord is currently pointing to the last character
  3120. * of the previous word, unless it backed up to the beginning
  3121. * of the buffer.
  3122. * Let's increment LastWord so that it points to the expeced
  3123. * insertion point.
  3124. */
  3125. ++LastWord;
  3126. }
  3127. CookedReadData->BufPtr = LastWord;
  3128. }
  3129. CookedReadData->CurrentPosition=(ULONG)(CookedReadData->BufPtr-CookedReadData->BackupLimit);
  3130. CurrentPosition = CookedReadData->OriginalCursorPosition;
  3131. // FE_SB
  3132. CurrentPosition.X = (SHORT)(CurrentPosition.X +
  3133. RetrieveTotalNumberOfSpaces(CookedReadData->OriginalCursorPosition.X,
  3134. CookedReadData->BackupLimit,
  3135. CookedReadData->CurrentPosition,
  3136. CookedReadData->ScreenInfo->Console));
  3137. if (CheckBisectStringW(CookedReadData->ScreenInfo,
  3138. CookedReadData->ScreenInfo->Console->CP,
  3139. CookedReadData->BackupLimit,
  3140. CookedReadData->CurrentPosition+1,
  3141. CookedReadData->ScreenInfo->ScreenBufferSize.X
  3142. -CookedReadData->OriginalCursorPosition.X
  3143. )) {
  3144. CurrentPosition.X++;
  3145. }
  3146. // end FE_SB
  3147. UpdateCursorPosition = TRUE;
  3148. }
  3149. } else {
  3150. if (CookedReadData->BufPtr != CookedReadData->BackupLimit) {
  3151. CookedReadData->BufPtr--;
  3152. CookedReadData->CurrentPosition--;
  3153. CurrentPosition.X = CookedReadData->ScreenInfo->BufferInfo.TextInfo.CursorPosition.X;
  3154. CurrentPosition.Y = CookedReadData->ScreenInfo->BufferInfo.TextInfo.CursorPosition.Y;
  3155. #if defined(FE_SB)
  3156. CurrentPosition.X = (SHORT)(CurrentPosition.X -
  3157. RetrieveNumberOfSpaces(CookedReadData->OriginalCursorPosition.X,
  3158. CookedReadData->BackupLimit,
  3159. CookedReadData->CurrentPosition,
  3160. CookedReadData->ScreenInfo->Console,
  3161. CookedReadData->ScreenInfo->Console->CP));
  3162. if (CheckBisectProcessW(CookedReadData->ScreenInfo,
  3163. CookedReadData->ScreenInfo->Console->CP,
  3164. CookedReadData->BackupLimit,
  3165. CookedReadData->CurrentPosition+2,
  3166. CookedReadData->ScreenInfo->ScreenBufferSize.X
  3167. -CookedReadData->OriginalCursorPosition.X,
  3168. CookedReadData->OriginalCursorPosition.X,
  3169. TRUE)) {
  3170. if ((CurrentPosition.X == -2) ||
  3171. (CurrentPosition.X == -1)) {
  3172. CurrentPosition.X--;
  3173. }
  3174. }
  3175. #else
  3176. CurrentPosition.X = (SHORT)(CurrentPosition.X - RetrieveNumberOfSpaces(CookedReadData->OriginalCursorPosition.X,
  3177. CookedReadData->BackupLimit,
  3178. CookedReadData->CurrentPosition));
  3179. #endif
  3180. UpdateCursorPosition = TRUE;
  3181. }
  3182. }
  3183. break;
  3184. case VK_RIGHT:
  3185. case VK_F1:
  3186. //
  3187. // we don't need to check for end of buffer here because we've
  3188. // already done it.
  3189. //
  3190. if (KeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) {
  3191. if (Char != VK_F1) {
  3192. if (CookedReadData->CurrentPosition < (CookedReadData->BytesRead/sizeof(WCHAR))) {
  3193. PWCHAR NextWord = CookedReadData->BufPtr;
  3194. if (!gExtendedEditKey) {
  3195. SHORT i;
  3196. for (i=(SHORT)(CookedReadData->CurrentPosition);
  3197. i<(SHORT)((CookedReadData->BytesRead-1)/sizeof(WCHAR));
  3198. i++) {
  3199. if (IS_WORD_DELIM(*NextWord)) {
  3200. i++;
  3201. NextWord++;
  3202. while ((i<(SHORT)((CookedReadData->BytesRead-1)/sizeof(WCHAR))) &&
  3203. IS_WORD_DELIM(*NextWord)) {
  3204. i++;
  3205. NextWord++;
  3206. }
  3207. break;
  3208. }
  3209. NextWord++;
  3210. }
  3211. } else {
  3212. /*
  3213. * A bit better word skipping.
  3214. */
  3215. PWCHAR BufLast = CookedReadData->BackupLimit + CookedReadData->BytesRead / sizeof(WCHAR);
  3216. UserAssert(NextWord < BufLast);
  3217. if (*NextWord == L' ') {
  3218. /*
  3219. * If the current character is space,
  3220. * skip to the next non-space character.
  3221. */
  3222. while (NextWord < BufLast) {
  3223. if (*NextWord != L' ') {
  3224. break;
  3225. }
  3226. ++NextWord;
  3227. }
  3228. } else {
  3229. /*
  3230. * Skip the body part.
  3231. */
  3232. BOOL fStartFromDelim = IS_WORD_DELIM(*NextWord);
  3233. while (++NextWord < BufLast) {
  3234. if (fStartFromDelim != IS_WORD_DELIM(*NextWord)) {
  3235. break;
  3236. }
  3237. }
  3238. /*
  3239. * Skip the space block.
  3240. */
  3241. if (NextWord < BufLast && *NextWord == L' ') {
  3242. while (++NextWord < BufLast) {
  3243. if (*NextWord != L' ') {
  3244. break;
  3245. }
  3246. }
  3247. }
  3248. }
  3249. }
  3250. CookedReadData->BufPtr = NextWord;
  3251. CookedReadData->CurrentPosition=(ULONG)(CookedReadData->BufPtr-CookedReadData->BackupLimit);
  3252. // FE_SB
  3253. CurrentPosition = CookedReadData->OriginalCursorPosition;
  3254. CurrentPosition.X = (SHORT)(CurrentPosition.X +
  3255. RetrieveTotalNumberOfSpaces(CookedReadData->OriginalCursorPosition.X,
  3256. CookedReadData->BackupLimit,
  3257. CookedReadData->CurrentPosition,
  3258. CookedReadData->ScreenInfo->Console));
  3259. if (CheckBisectStringW(CookedReadData->ScreenInfo,
  3260. CookedReadData->ScreenInfo->Console->CP,
  3261. CookedReadData->BackupLimit,
  3262. CookedReadData->CurrentPosition+1,
  3263. CookedReadData->ScreenInfo->ScreenBufferSize.X
  3264. -CookedReadData->OriginalCursorPosition.X
  3265. )) {
  3266. CurrentPosition.X++;
  3267. }
  3268. // end FE_SB
  3269. UpdateCursorPosition = TRUE;
  3270. }
  3271. }
  3272. } else {
  3273. //
  3274. // if not at the end of the line, move cursor position right
  3275. //
  3276. if (CookedReadData->CurrentPosition < (CookedReadData->BytesRead/sizeof(WCHAR))) {
  3277. CurrentPosition = CookedReadData->ScreenInfo->BufferInfo.TextInfo.CursorPosition;
  3278. #if defined(FE_SB)
  3279. CurrentPosition.X = (SHORT)(CurrentPosition.X +
  3280. RetrieveNumberOfSpaces(CookedReadData->OriginalCursorPosition.X,
  3281. CookedReadData->BackupLimit,
  3282. CookedReadData->CurrentPosition,
  3283. CookedReadData->ScreenInfo->Console,
  3284. CookedReadData->ScreenInfo->Console->CP));
  3285. if (CheckBisectProcessW(CookedReadData->ScreenInfo,
  3286. CookedReadData->ScreenInfo->Console->CP,
  3287. CookedReadData->BackupLimit,
  3288. CookedReadData->CurrentPosition+2,
  3289. CookedReadData->ScreenInfo->ScreenBufferSize.X
  3290. -CookedReadData->OriginalCursorPosition.X,
  3291. CookedReadData->OriginalCursorPosition.X,
  3292. TRUE)) {
  3293. if (CurrentPosition.X == (CookedReadData->ScreenInfo->ScreenBufferSize.X-1))
  3294. CurrentPosition.X++;
  3295. }
  3296. #else
  3297. CurrentPosition.X = (SHORT)(CurrentPosition.X + RetrieveNumberOfSpaces(CookedReadData->OriginalCursorPosition.X,
  3298. CookedReadData->BackupLimit,
  3299. CookedReadData->CurrentPosition));
  3300. #endif
  3301. CookedReadData->BufPtr++;
  3302. CookedReadData->CurrentPosition++;
  3303. UpdateCursorPosition = TRUE;
  3304. //
  3305. // if at the end of the line, copy a character from the
  3306. // same position in the last command
  3307. //
  3308. } else if (CookedReadData->CommandHistory) {
  3309. PCOMMAND LastCommand;
  3310. DWORD NumSpaces;
  3311. LastCommand = GetLastCommand(CookedReadData->CommandHistory);
  3312. if (LastCommand && (USHORT)(LastCommand->CommandLength/sizeof(WCHAR)) > (USHORT)CookedReadData->CurrentPosition) {
  3313. *CookedReadData->BufPtr = LastCommand->Command[CookedReadData->CurrentPosition];
  3314. CookedReadData->BytesRead += sizeof(WCHAR);
  3315. CookedReadData->CurrentPosition++;
  3316. if (CookedReadData->Echo) {
  3317. CharsToWrite = sizeof(WCHAR);
  3318. Status = WriteCharsFromInput(
  3319. CookedReadData->ScreenInfo,
  3320. CookedReadData->BackupLimit,
  3321. CookedReadData->BufPtr,
  3322. CookedReadData->BufPtr,
  3323. &CharsToWrite,
  3324. (PLONG)&NumSpaces,
  3325. CookedReadData->OriginalCursorPosition.X,
  3326. WC_DESTRUCTIVE_BACKSPACE |
  3327. WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
  3328. &ScrollY);
  3329. UserAssert(NT_SUCCESS(Status));
  3330. CookedReadData->OriginalCursorPosition.Y += ScrollY;
  3331. CookedReadData->NumberOfVisibleChars += NumSpaces;
  3332. }
  3333. CookedReadData->BufPtr+=1;
  3334. }
  3335. }
  3336. }
  3337. break;
  3338. case VK_F2:
  3339. //
  3340. // copy the previous command to the current command, up to but
  3341. // not including the character specified by the user. the user
  3342. // is prompted via popup to enter a character.
  3343. //
  3344. if (CookedReadData->CommandHistory) {
  3345. COORD PopupSize;
  3346. PopupSize.X = COPY_TO_CHAR_PROMPT_LENGTH+2;
  3347. PopupSize.Y = 1;
  3348. Status = BeginPopup(CookedReadData->ScreenInfo,
  3349. CookedReadData->CommandHistory,
  3350. PopupSize
  3351. );
  3352. if (NT_SUCCESS(Status)) {
  3353. // CopyToCharPopup does EndPopup call
  3354. return CopyToCharPopup(CookedReadData,
  3355. WaitReplyMessage,
  3356. WaitingThread,
  3357. WaitRoutine
  3358. );
  3359. }
  3360. }
  3361. break;
  3362. case VK_F3:
  3363. //
  3364. // copy the remainder of the previous command to the current command.
  3365. //
  3366. if (CookedReadData->CommandHistory) {
  3367. PCOMMAND LastCommand;
  3368. DWORD NumSpaces;
  3369. int j; // chars, not bytes
  3370. LastCommand = GetLastCommand(CookedReadData->CommandHistory);
  3371. if (LastCommand && (USHORT)(LastCommand->CommandLength/sizeof(WCHAR)) > (USHORT)CookedReadData->CurrentPosition) {
  3372. j = (LastCommand->CommandLength/sizeof(WCHAR)) - CookedReadData->CurrentPosition;
  3373. RtlCopyMemory(CookedReadData->BufPtr,
  3374. &LastCommand->Command[CookedReadData->CurrentPosition],
  3375. j*sizeof(WCHAR)
  3376. );
  3377. CookedReadData->CurrentPosition += j;
  3378. j *= sizeof(WCHAR);
  3379. CookedReadData->BytesRead += j;
  3380. if (CookedReadData->Echo) {
  3381. Status = WriteCharsFromInput(CookedReadData->ScreenInfo,
  3382. CookedReadData->BackupLimit,
  3383. CookedReadData->BufPtr,
  3384. CookedReadData->BufPtr,
  3385. (PDWORD) &j,
  3386. (PLONG)&NumSpaces,
  3387. CookedReadData->OriginalCursorPosition.X,
  3388. WC_DESTRUCTIVE_BACKSPACE |
  3389. WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
  3390. &ScrollY);
  3391. UserAssert(NT_SUCCESS(Status));
  3392. CookedReadData->OriginalCursorPosition.Y += ScrollY;
  3393. CookedReadData->NumberOfVisibleChars += NumSpaces;
  3394. }
  3395. CookedReadData->BufPtr+=j/sizeof(WCHAR);
  3396. }
  3397. }
  3398. break;
  3399. case VK_F4:
  3400. //
  3401. // copy the previous command to the current command, from
  3402. // the letter specified by the user. the user
  3403. // is prompted via popup to enter a character.
  3404. //
  3405. if (CookedReadData->CommandHistory) {
  3406. COORD PopupSize;
  3407. PopupSize.X = COPY_FROM_CHAR_PROMPT_LENGTH+2;
  3408. PopupSize.Y = 1;
  3409. Status = BeginPopup(CookedReadData->ScreenInfo,
  3410. CookedReadData->CommandHistory,
  3411. PopupSize
  3412. );
  3413. if (NT_SUCCESS(Status)) {
  3414. // CopyFromCharPopup does EndPopup call
  3415. return CopyFromCharPopup(CookedReadData,
  3416. WaitReplyMessage,
  3417. WaitingThread,
  3418. WaitRoutine
  3419. );
  3420. }
  3421. }
  3422. break;
  3423. case VK_F6:
  3424. //
  3425. // place a ctrl-z in the current command line
  3426. //
  3427. {
  3428. DWORD NumSpaces;
  3429. *CookedReadData->BufPtr = (WCHAR)0x1a; // ctrl-z
  3430. CookedReadData->BytesRead += sizeof(WCHAR);
  3431. CookedReadData->CurrentPosition++;
  3432. if (CookedReadData->Echo) {
  3433. CharsToWrite = sizeof(WCHAR);
  3434. Status = WriteCharsFromInput(CookedReadData->ScreenInfo,
  3435. CookedReadData->BackupLimit,
  3436. CookedReadData->BufPtr,
  3437. CookedReadData->BufPtr,
  3438. &CharsToWrite,
  3439. (PLONG)&NumSpaces,
  3440. CookedReadData->OriginalCursorPosition.X,
  3441. WC_DESTRUCTIVE_BACKSPACE |
  3442. WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
  3443. &ScrollY
  3444. );
  3445. UserAssert(NT_SUCCESS(Status));
  3446. CookedReadData->OriginalCursorPosition.Y += ScrollY;
  3447. CookedReadData->NumberOfVisibleChars += NumSpaces;
  3448. }
  3449. CookedReadData->BufPtr+=1;
  3450. }
  3451. break;
  3452. case VK_F7:
  3453. if (KeyState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)) {
  3454. if (CookedReadData->CommandHistory) {
  3455. EmptyCommandHistory(CookedReadData->CommandHistory);
  3456. CookedReadData->CommandHistory->Flags |= CLE_ALLOCATED;
  3457. }
  3458. }
  3459. break;
  3460. case VK_F8:
  3461. if (CookedReadData->CommandHistory) {
  3462. SHORT i;
  3463. //
  3464. // cycles through the stored commands that start with
  3465. // the characters in the current command
  3466. //
  3467. i = FindMatchingCommand(CookedReadData->CommandHistory,
  3468. CookedReadData->BackupLimit,
  3469. CookedReadData->CurrentPosition*sizeof(WCHAR),
  3470. CookedReadData->CommandHistory->LastDisplayed, 0);
  3471. if (i!=-1) {
  3472. SHORT CurrentPosition;
  3473. COORD CursorPosition;
  3474. //
  3475. // save cursor position
  3476. //
  3477. CurrentPosition = (SHORT)CookedReadData->CurrentPosition;
  3478. CursorPosition = CookedReadData->ScreenInfo->BufferInfo.TextInfo.CursorPosition;
  3479. DeleteCommandLine(CookedReadData,
  3480. TRUE);
  3481. Status = RetrieveNthCommand(CookedReadData->CommandHistory,
  3482. i,
  3483. CookedReadData->BackupLimit,
  3484. CookedReadData->BufferSize,
  3485. &CookedReadData->BytesRead);
  3486. UserAssert(CookedReadData->BackupLimit == CookedReadData->BufPtr);
  3487. if (CookedReadData->Echo) {
  3488. Status = WriteCharsFromInput(CookedReadData->ScreenInfo,
  3489. CookedReadData->BackupLimit,
  3490. CookedReadData->BufPtr,
  3491. CookedReadData->BufPtr,
  3492. &CookedReadData->BytesRead,
  3493. (PLONG)&CookedReadData->NumberOfVisibleChars,
  3494. CookedReadData->OriginalCursorPosition.X,
  3495. WC_DESTRUCTIVE_BACKSPACE |
  3496. WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
  3497. &ScrollY);
  3498. UserAssert(NT_SUCCESS(Status));
  3499. CookedReadData->OriginalCursorPosition.Y += ScrollY;
  3500. }
  3501. CursorPosition.Y += ScrollY;
  3502. //
  3503. // restore cursor position
  3504. //
  3505. CookedReadData->BufPtr = CookedReadData->BackupLimit + CurrentPosition;
  3506. CookedReadData->CurrentPosition = CurrentPosition;
  3507. Status = SetCursorPosition(CookedReadData->ScreenInfo,
  3508. CursorPosition,
  3509. TRUE);
  3510. UserAssert(NT_SUCCESS(Status));
  3511. }
  3512. }
  3513. break;
  3514. case VK_F9:
  3515. //
  3516. // prompt the user to enter the desired command number.
  3517. // copy that command to the command line.
  3518. //
  3519. {
  3520. COORD PopupSize;
  3521. if (CookedReadData->CommandHistory &&
  3522. CookedReadData->CommandHistory->NumberOfCommands &&
  3523. CookedReadData->ScreenInfo->ScreenBufferSize.X >= MINIMUM_COMMAND_PROMPT_SIZE+2) { // 2 is for border
  3524. PopupSize.X = COMMAND_NUMBER_PROMPT_LENGTH+COMMAND_NUMBER_LENGTH;
  3525. PopupSize.Y = 1;
  3526. Status = BeginPopup(CookedReadData->ScreenInfo,
  3527. CookedReadData->CommandHistory,
  3528. PopupSize
  3529. );
  3530. if (NT_SUCCESS(Status)) {
  3531. // CommandNumberPopup does EndPopup call
  3532. return CommandNumberPopup(CookedReadData,
  3533. WaitReplyMessage,
  3534. WaitingThread,
  3535. WaitRoutine
  3536. );
  3537. }
  3538. }
  3539. }
  3540. break;
  3541. case VK_F10:
  3542. if (KeyState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)) {
  3543. ClearAliases(CookedReadData->Console);
  3544. }
  3545. break;
  3546. case VK_INSERT:
  3547. CookedReadData->InsertMode = !CookedReadData->InsertMode;
  3548. SetCursorMode(CookedReadData->ScreenInfo,
  3549. (BOOLEAN)(CookedReadData->InsertMode != CookedReadData->Console->InsertMode));
  3550. break;
  3551. case VK_DELETE:
  3552. if (!AT_EOL(CookedReadData)) {
  3553. COORD CursorPosition;
  3554. fStartFromDelim = IS_WORD_DELIM(*CookedReadData->BufPtr);
  3555. del_repeat:
  3556. //
  3557. // save cursor position
  3558. //
  3559. CursorPosition = CookedReadData->ScreenInfo->BufferInfo.TextInfo.CursorPosition;
  3560. //
  3561. // deletecommandline
  3562. //
  3563. DeleteCommandLine(CookedReadData,
  3564. FALSE);
  3565. //
  3566. // delete char
  3567. //
  3568. CookedReadData->BytesRead -= sizeof(WCHAR);
  3569. RtlCopyMemory(CookedReadData->BufPtr,
  3570. CookedReadData->BufPtr+1,
  3571. CookedReadData->BytesRead - (CookedReadData->CurrentPosition*sizeof(WCHAR))
  3572. );
  3573. #if defined(FE_SB)
  3574. {
  3575. PWCHAR buf = (PWCHAR)((PBYTE)CookedReadData->BackupLimit +
  3576. CookedReadData->BytesRead );
  3577. *buf = (WCHAR)' ';
  3578. }
  3579. #endif
  3580. //
  3581. // write commandline
  3582. //
  3583. if (CookedReadData->Echo) {
  3584. Status = WriteCharsFromInput(CookedReadData->ScreenInfo,
  3585. CookedReadData->BackupLimit,
  3586. CookedReadData->BackupLimit,
  3587. CookedReadData->BackupLimit,
  3588. &CookedReadData->BytesRead,
  3589. (PLONG)&CookedReadData->NumberOfVisibleChars,
  3590. CookedReadData->OriginalCursorPosition.X,
  3591. WC_DESTRUCTIVE_BACKSPACE |
  3592. WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
  3593. NULL);
  3594. UserAssert(NT_SUCCESS(Status));
  3595. }
  3596. //
  3597. // restore cursor position
  3598. //
  3599. if (CONSOLE_IS_DBCS_ENABLED() && CONSOLE_IS_DBCS_CP(CookedReadData->Console)) {
  3600. if (CheckBisectProcessW(CookedReadData->ScreenInfo,
  3601. CookedReadData->ScreenInfo->Console->CP,
  3602. CookedReadData->BackupLimit,
  3603. CookedReadData->CurrentPosition+1,
  3604. CookedReadData->ScreenInfo->ScreenBufferSize.X
  3605. -CookedReadData->OriginalCursorPosition.X,
  3606. CookedReadData->OriginalCursorPosition.X,
  3607. TRUE)) {
  3608. CursorPosition.X++;
  3609. }
  3610. CurrentPosition = CursorPosition;
  3611. if (CookedReadData->Echo) {
  3612. Status = AdjustCursorPosition(CookedReadData->ScreenInfo,
  3613. CurrentPosition,
  3614. TRUE,
  3615. NULL);
  3616. UserAssert(NT_SUCCESS(Status));
  3617. }
  3618. } else {
  3619. Status = SetCursorPosition(CookedReadData->ScreenInfo,
  3620. CursorPosition,
  3621. TRUE);
  3622. UserAssert(NT_SUCCESS(Status));
  3623. }
  3624. // If Ctrl key is pressed, delete a word.
  3625. // If the start point was word delimiter, just remove delimiters portion only.
  3626. if ((KeyState & CTRL_PRESSED) && !AT_EOL(CookedReadData) &&
  3627. fStartFromDelim ^ !IS_WORD_DELIM(*CookedReadData->BufPtr)) {
  3628. DBGPRINT(("Repeating it(%x).\n", *CookedReadData->BufPtr));
  3629. goto del_repeat;
  3630. }
  3631. }
  3632. break;
  3633. default:
  3634. UserAssert(FALSE);
  3635. break;
  3636. }
  3637. }
  3638. if (UpdateCursorPosition && CookedReadData->Echo) {
  3639. Status = AdjustCursorPosition(CookedReadData->ScreenInfo,
  3640. CurrentPosition,
  3641. TRUE,
  3642. NULL);
  3643. UserAssert(NT_SUCCESS(Status));
  3644. }
  3645. return STATUS_SUCCESS;
  3646. }
  3647. PCOMMAND RemoveCommand(
  3648. IN PCOMMAND_HISTORY CommandHistory,
  3649. IN SHORT iDel)
  3650. {
  3651. SHORT iFirst, iLast, iDisp, nDel;
  3652. PCOMMAND *ppcFirst, *ppcDel, pcmdDel;
  3653. iFirst = CommandHistory->FirstCommand;
  3654. iLast = CommandHistory->LastAdded;
  3655. iDisp = CommandHistory->LastDisplayed;
  3656. if (CommandHistory->NumberOfCommands == 0) {
  3657. return NULL;
  3658. }
  3659. nDel = COMMAND_INDEX_TO_NUM(iDel, CommandHistory);
  3660. if ((nDel < COMMAND_INDEX_TO_NUM(iFirst, CommandHistory)) ||
  3661. (nDel > COMMAND_INDEX_TO_NUM(iLast, CommandHistory))) {
  3662. return NULL;
  3663. }
  3664. if (iDisp == iDel) {
  3665. CommandHistory->LastDisplayed = -1;
  3666. }
  3667. ppcFirst = &(CommandHistory->Commands[iFirst]);
  3668. ppcDel = &(CommandHistory->Commands[iDel]);
  3669. pcmdDel = *ppcDel;
  3670. if (iDel < iLast) {
  3671. RtlCopyMemory(ppcDel, ppcDel+1, (iLast - iDel) * sizeof(PCOMMAND));
  3672. if ((iDisp > iDel) && (iDisp <= iLast)) {
  3673. COMMAND_IND_DEC(iDisp, CommandHistory);
  3674. }
  3675. COMMAND_IND_DEC(iLast, CommandHistory);
  3676. } else if (iFirst <= iDel) {
  3677. RtlMoveMemory(ppcFirst+1, ppcFirst, (iDel - iFirst) * sizeof(PCOMMAND));
  3678. if ((iDisp >= iFirst) && (iDisp < iDel)) {
  3679. COMMAND_IND_INC(iDisp, CommandHistory);
  3680. }
  3681. COMMAND_IND_INC(iFirst, CommandHistory);
  3682. }
  3683. CommandHistory->FirstCommand = iFirst;
  3684. CommandHistory->LastAdded = iLast;
  3685. CommandHistory->LastDisplayed = iDisp;
  3686. CommandHistory->NumberOfCommands--;
  3687. return pcmdDel;
  3688. }
  3689. SHORT
  3690. FindMatchingCommand(
  3691. IN PCOMMAND_HISTORY CommandHistory,
  3692. IN PWCHAR pwszIn,
  3693. IN ULONG cbIn, // in bytes (!)
  3694. IN SHORT CommandIndex, // where to start from
  3695. IN DWORD Flags
  3696. )
  3697. /*++
  3698. this routine finds the most recent command that starts with
  3699. the letters already in the current command. it returns the
  3700. array index (no mod needed).
  3701. --*/
  3702. {
  3703. SHORT i;
  3704. if (CommandHistory->NumberOfCommands == 0) {
  3705. return -1;
  3706. }
  3707. if (!(Flags & FMCFL_JUST_LOOKING) && (CommandHistory->Flags & CLE_RESET)) {
  3708. CommandHistory->Flags &= ~CLE_RESET;
  3709. } else {
  3710. COMMAND_IND_PREV(CommandIndex, CommandHistory);
  3711. }
  3712. if (cbIn == 0) {
  3713. return CommandIndex;
  3714. }
  3715. for (i=0;i<CommandHistory->NumberOfCommands;i++) {
  3716. PCOMMAND pcmdT = CommandHistory->Commands[CommandIndex];
  3717. if ((!(Flags & FMCFL_EXACT_MATCH) && (cbIn <= pcmdT->CommandLength)) ||
  3718. ((USHORT)cbIn == pcmdT->CommandLength)) {
  3719. if (!my_wcsncmp(pcmdT->Command, pwszIn, (USHORT)cbIn)) {
  3720. return CommandIndex;
  3721. }
  3722. }
  3723. COMMAND_IND_PREV(CommandIndex, CommandHistory);
  3724. }
  3725. return -1;
  3726. }
  3727. VOID
  3728. DrawCommandListBorder(
  3729. IN PCLE_POPUP Popup,
  3730. IN PSCREEN_INFORMATION ScreenInfo
  3731. )
  3732. {
  3733. COORD WriteCoord;
  3734. ULONG Length;
  3735. SHORT i;
  3736. //
  3737. // fill attributes of top line
  3738. //
  3739. WriteCoord.X = Popup->Region.Left;
  3740. WriteCoord.Y = Popup->Region.Top;
  3741. Length = POPUP_SIZE_X(Popup) + 2;
  3742. FillOutput(ScreenInfo,
  3743. Popup->Attributes,
  3744. WriteCoord,
  3745. CONSOLE_ATTRIBUTE,
  3746. &Length
  3747. );
  3748. //
  3749. // draw upper left corner
  3750. //
  3751. Length = 1;
  3752. FillOutput(ScreenInfo,
  3753. #if defined(FE_SB)
  3754. ScreenInfo->LineChar[UPPER_LEFT_CORNER],
  3755. #else
  3756. (WCHAR)0x250c,
  3757. #endif
  3758. WriteCoord,
  3759. CONSOLE_REAL_UNICODE,
  3760. &Length
  3761. );
  3762. //
  3763. // draw upper bar
  3764. //
  3765. WriteCoord.X += 1;
  3766. Length = POPUP_SIZE_X(Popup);
  3767. FillOutput(ScreenInfo,
  3768. #if defined(FE_SB)
  3769. ScreenInfo->LineChar[HORIZONTAL_LINE],
  3770. #else
  3771. (WCHAR)0x2500,
  3772. #endif
  3773. WriteCoord,
  3774. CONSOLE_REAL_UNICODE,
  3775. &Length
  3776. );
  3777. //
  3778. // draw upper right corner
  3779. //
  3780. WriteCoord.X = Popup->Region.Right;
  3781. Length = 1;
  3782. FillOutput(ScreenInfo,
  3783. #if defined(FE_SB)
  3784. ScreenInfo->LineChar[UPPER_RIGHT_CORNER],
  3785. #else
  3786. (WCHAR)0x2510,
  3787. #endif
  3788. WriteCoord,
  3789. CONSOLE_REAL_UNICODE,
  3790. &Length
  3791. );
  3792. for (i=0;i<POPUP_SIZE_Y(Popup);i++) {
  3793. WriteCoord.Y += 1;
  3794. WriteCoord.X = Popup->Region.Left;
  3795. //
  3796. // fill attributes
  3797. //
  3798. Length = POPUP_SIZE_X(Popup) + 2;
  3799. FillOutput(ScreenInfo,
  3800. Popup->Attributes,
  3801. WriteCoord,
  3802. CONSOLE_ATTRIBUTE,
  3803. &Length
  3804. );
  3805. Length = 1;
  3806. FillOutput(ScreenInfo,
  3807. #if defined(FE_SB)
  3808. ScreenInfo->LineChar[VERTICAL_LINE],
  3809. #else
  3810. (WCHAR)0x2502,
  3811. #endif
  3812. WriteCoord,
  3813. CONSOLE_REAL_UNICODE,
  3814. &Length
  3815. );
  3816. WriteCoord.X = Popup->Region.Right;
  3817. Length = 1;
  3818. FillOutput(ScreenInfo,
  3819. #if defined(FE_SB)
  3820. ScreenInfo->LineChar[VERTICAL_LINE],
  3821. #else
  3822. (WCHAR)0x2502,
  3823. #endif
  3824. WriteCoord,
  3825. CONSOLE_REAL_UNICODE,
  3826. &Length
  3827. );
  3828. }
  3829. //
  3830. // draw bottom line
  3831. //
  3832. // fill attributes of top line
  3833. //
  3834. WriteCoord.X = Popup->Region.Left;
  3835. WriteCoord.Y = Popup->Region.Bottom;
  3836. Length = POPUP_SIZE_X(Popup) + 2;
  3837. FillOutput(ScreenInfo,
  3838. Popup->Attributes,
  3839. WriteCoord,
  3840. CONSOLE_ATTRIBUTE,
  3841. &Length
  3842. );
  3843. //
  3844. // draw bottom left corner
  3845. //
  3846. Length = 1;
  3847. WriteCoord.X = Popup->Region.Left;
  3848. FillOutput(ScreenInfo,
  3849. #if defined(FE_SB)
  3850. ScreenInfo->LineChar[BOTTOM_LEFT_CORNER],
  3851. #else
  3852. (WCHAR)0x2514,
  3853. #endif
  3854. WriteCoord,
  3855. CONSOLE_REAL_UNICODE,
  3856. &Length
  3857. );
  3858. //
  3859. // draw lower bar
  3860. //
  3861. WriteCoord.X += 1;
  3862. Length = POPUP_SIZE_X(Popup);
  3863. FillOutput(ScreenInfo,
  3864. #if defined(FE_SB)
  3865. ScreenInfo->LineChar[HORIZONTAL_LINE],
  3866. #else
  3867. (WCHAR)0x2500,
  3868. #endif
  3869. WriteCoord,
  3870. CONSOLE_REAL_UNICODE,
  3871. &Length
  3872. );
  3873. //
  3874. // draw lower right corner
  3875. //
  3876. WriteCoord.X = Popup->Region.Right;
  3877. Length = 1;
  3878. FillOutput(ScreenInfo,
  3879. #if defined(FE_SB)
  3880. ScreenInfo->LineChar[BOTTOM_RIGHT_CORNER],
  3881. #else
  3882. (WCHAR)0x2518,
  3883. #endif
  3884. WriteCoord,
  3885. CONSOLE_REAL_UNICODE,
  3886. &Length
  3887. );
  3888. }
  3889. VOID
  3890. UpdateHighlight(
  3891. IN PCLE_POPUP Popup,
  3892. IN SHORT OldCurrentCommand, // command number, not index
  3893. IN SHORT NewCurrentCommand,
  3894. IN PSCREEN_INFORMATION ScreenInfo
  3895. )
  3896. {
  3897. COORD WriteCoord;
  3898. ULONG lStringLength;
  3899. WORD Attributes;
  3900. SHORT TopIndex;
  3901. if (Popup->BottomIndex < POPUP_SIZE_Y(Popup)) {
  3902. TopIndex = 0;
  3903. } else {
  3904. TopIndex = (SHORT)(Popup->BottomIndex-POPUP_SIZE_Y(Popup)+1);
  3905. }
  3906. WriteCoord.X = (SHORT)(Popup->Region.Left+1);
  3907. lStringLength = POPUP_SIZE_X(Popup);
  3908. WriteCoord.Y = (SHORT)(Popup->Region.Top+1+OldCurrentCommand-TopIndex);
  3909. FillOutput(ScreenInfo,
  3910. Popup->Attributes,
  3911. WriteCoord,
  3912. CONSOLE_ATTRIBUTE,
  3913. &lStringLength
  3914. );
  3915. //
  3916. // highlight new command
  3917. //
  3918. WriteCoord.Y = (SHORT)(Popup->Region.Top+1+NewCurrentCommand-TopIndex);
  3919. // inverted attributes
  3920. Attributes = (WORD)(((Popup->Attributes << 4) & 0xf0) |
  3921. ((Popup->Attributes >> 4) & 0x0f));
  3922. FillOutput(ScreenInfo,
  3923. Attributes,
  3924. WriteCoord,
  3925. CONSOLE_ATTRIBUTE,
  3926. &lStringLength
  3927. );
  3928. }
  3929. VOID
  3930. DrawCommandListPopup(
  3931. IN PCLE_POPUP Popup,
  3932. IN SHORT CurrentCommand,
  3933. IN PCOMMAND_HISTORY CommandHistory,
  3934. IN PSCREEN_INFORMATION ScreenInfo
  3935. )
  3936. {
  3937. WORD Attributes;
  3938. ULONG lStringLength,CommandNumberLength;
  3939. CHAR CommandNumber[COMMAND_NUMBER_SIZE];
  3940. PCHAR CommandNumberPtr;
  3941. COORD WriteCoord;
  3942. SHORT i;
  3943. //
  3944. // draw empty popup
  3945. //
  3946. WriteCoord.X = (SHORT)(Popup->Region.Left+1);
  3947. WriteCoord.Y = (SHORT)(Popup->Region.Top+1);
  3948. lStringLength = POPUP_SIZE_X(Popup);
  3949. for (i=0;i<POPUP_SIZE_Y(Popup);i++) {
  3950. FillOutput(ScreenInfo,
  3951. Popup->Attributes,
  3952. WriteCoord,
  3953. CONSOLE_ATTRIBUTE,
  3954. &lStringLength
  3955. );
  3956. FillOutput(ScreenInfo,
  3957. (WCHAR)' ',
  3958. WriteCoord,
  3959. CONSOLE_FALSE_UNICODE, // faster than real unicode
  3960. &lStringLength
  3961. );
  3962. WriteCoord.Y += 1;
  3963. }
  3964. WriteCoord.Y = (SHORT)(Popup->Region.Top+1);
  3965. for (i=max((SHORT)(Popup->BottomIndex-POPUP_SIZE_Y(Popup)+1),0);i<=Popup->BottomIndex;i++) {
  3966. //
  3967. // write command number to screen
  3968. //
  3969. CommandNumberPtr = _itoa(i,CommandNumber,10);
  3970. CommandNumberLength = (SHORT)lstrlenA(CommandNumberPtr);
  3971. CommandNumber[CommandNumberLength] = ':';
  3972. CommandNumber[CommandNumberLength+1] = ' ';
  3973. CommandNumberLength+=2;
  3974. if (CommandNumberLength > (ULONG)POPUP_SIZE_X(Popup))
  3975. CommandNumberLength = (ULONG)POPUP_SIZE_X(Popup);
  3976. WriteCoord.X = (SHORT)(Popup->Region.Left+1);
  3977. WriteOutputString(ScreenInfo,
  3978. CommandNumberPtr,
  3979. WriteCoord,
  3980. CONSOLE_ASCII,
  3981. &CommandNumberLength,
  3982. NULL
  3983. );
  3984. //
  3985. // write command to screen
  3986. //
  3987. lStringLength = CommandHistory->Commands[COMMAND_NUM_TO_INDEX(i,CommandHistory)]->CommandLength/sizeof(WCHAR);
  3988. #if defined(FE_SB)
  3989. {
  3990. DWORD lTmpStringLength;
  3991. LONG lPopupLength;
  3992. LPWSTR lpStr;
  3993. lTmpStringLength = lStringLength;
  3994. lPopupLength = POPUP_SIZE_X(Popup) - CommandNumberLength;
  3995. lpStr = CommandHistory->Commands[COMMAND_NUM_TO_INDEX(i,CommandHistory)]->Command;
  3996. while (lTmpStringLength--) {
  3997. if (IsConsoleFullWidth(ScreenInfo->Console->hDC,
  3998. ScreenInfo->Console->OutputCP,*lpStr++)) {
  3999. lPopupLength -= 2;
  4000. } else {
  4001. lPopupLength--;
  4002. }
  4003. if (lPopupLength <= 0) {
  4004. lStringLength -= lTmpStringLength;
  4005. if (lPopupLength < 0)
  4006. lStringLength--;
  4007. break;
  4008. }
  4009. }
  4010. }
  4011. #else
  4012. if ((lStringLength+CommandNumberLength) > (ULONG)POPUP_SIZE_X(Popup))
  4013. lStringLength = (ULONG)(POPUP_SIZE_X(Popup)-CommandNumberLength);
  4014. #endif
  4015. WriteCoord.X = (SHORT)(WriteCoord.X + CommandNumberLength);
  4016. #if defined(FE_SB)
  4017. {
  4018. PWCHAR TransBuffer;
  4019. TransBuffer = ConsoleHeapAlloc(TMP_DBCS_TAG, lStringLength * sizeof(WCHAR));
  4020. if (TransBuffer == NULL) {
  4021. return;
  4022. }
  4023. RtlCopyMemory(TransBuffer,CommandHistory->Commands[COMMAND_NUM_TO_INDEX(i,CommandHistory)]->Command,lStringLength * sizeof(WCHAR));
  4024. WriteOutputString(ScreenInfo,
  4025. TransBuffer,
  4026. WriteCoord,
  4027. CONSOLE_REAL_UNICODE,
  4028. &lStringLength,
  4029. NULL
  4030. );
  4031. ConsoleHeapFree(TransBuffer);
  4032. }
  4033. #else
  4034. WriteOutputString(ScreenInfo,
  4035. CommandHistory->Commands[COMMAND_NUM_TO_INDEX(i,CommandHistory)]->Command,
  4036. WriteCoord,
  4037. CONSOLE_REAL_UNICODE,
  4038. &lStringLength,
  4039. NULL
  4040. );
  4041. // convert back to true unicode (got converted by WriteOutputString)
  4042. if ((ScreenInfo->Flags & CONSOLE_OEMFONT_DISPLAY) &&
  4043. !(ScreenInfo->Console->FullScreenFlags & CONSOLE_FULLSCREEN)) {
  4044. FalseUnicodeToRealUnicode(CommandHistory->Commands[COMMAND_NUM_TO_INDEX(i,CommandHistory)]->Command,
  4045. lStringLength,
  4046. ScreenInfo->Console->OutputCP);
  4047. }
  4048. #endif
  4049. //
  4050. // write attributes to screen
  4051. //
  4052. if (COMMAND_NUM_TO_INDEX(i,CommandHistory) == CurrentCommand) {
  4053. WriteCoord.X = (SHORT)(Popup->Region.Left+1);
  4054. // inverted attributes
  4055. Attributes = (WORD)(((Popup->Attributes << 4) & 0xf0) |
  4056. ((Popup->Attributes >> 4) & 0x0f));
  4057. lStringLength = POPUP_SIZE_X(Popup);
  4058. FillOutput(ScreenInfo,
  4059. Attributes,
  4060. WriteCoord,
  4061. CONSOLE_ATTRIBUTE,
  4062. &lStringLength
  4063. );
  4064. }
  4065. WriteCoord.Y += 1;
  4066. }
  4067. }
  4068. VOID
  4069. UpdateCommandListPopup(
  4070. IN SHORT Delta,
  4071. IN OUT PSHORT CurrentCommand, // real index, not command #
  4072. IN PCOMMAND_HISTORY CommandHistory,
  4073. IN PCLE_POPUP Popup,
  4074. IN PSCREEN_INFORMATION ScreenInfo,
  4075. IN DWORD Flags
  4076. )
  4077. {
  4078. SHORT Size;
  4079. SHORT CurCmdNum;
  4080. SHORT NewCmdNum;
  4081. BOOL Scroll=FALSE;
  4082. if (Delta == 0) {
  4083. return;
  4084. }
  4085. Size = POPUP_SIZE_Y(Popup);
  4086. if (Flags & UCLP_WRAP) {
  4087. CurCmdNum = *CurrentCommand;
  4088. NewCmdNum = CurCmdNum + Delta;
  4089. NewCmdNum = COMMAND_INDEX_TO_NUM(NewCmdNum, CommandHistory);
  4090. CurCmdNum = COMMAND_INDEX_TO_NUM(CurCmdNum, CommandHistory);
  4091. } else {
  4092. CurCmdNum = COMMAND_INDEX_TO_NUM(*CurrentCommand, CommandHistory);
  4093. NewCmdNum = CurCmdNum + Delta;
  4094. if (NewCmdNum >= CommandHistory->NumberOfCommands) {
  4095. NewCmdNum = (SHORT)(CommandHistory->NumberOfCommands-1);
  4096. } else if (NewCmdNum < 0) {
  4097. NewCmdNum = 0;
  4098. }
  4099. }
  4100. Delta = NewCmdNum - CurCmdNum;
  4101. // determine amount to scroll, if any
  4102. if (NewCmdNum <= Popup->BottomIndex-Size) {
  4103. Popup->BottomIndex += Delta;
  4104. if (Popup->BottomIndex < (SHORT)(Size-1)) {
  4105. Popup->BottomIndex = (SHORT)(Size-1);
  4106. }
  4107. Scroll = TRUE;
  4108. } else if (NewCmdNum > Popup->BottomIndex) {
  4109. Popup->BottomIndex += Delta;
  4110. if (Popup->BottomIndex >= CommandHistory->NumberOfCommands) {
  4111. Popup->BottomIndex = (SHORT)(CommandHistory->NumberOfCommands-1);
  4112. }
  4113. Scroll = TRUE;
  4114. }
  4115. //
  4116. // write commands to popup
  4117. //
  4118. if (Scroll) {
  4119. DrawCommandListPopup(Popup,COMMAND_NUM_TO_INDEX(NewCmdNum,CommandHistory),CommandHistory,ScreenInfo);
  4120. } else {
  4121. UpdateHighlight(Popup,COMMAND_INDEX_TO_NUM((*CurrentCommand),CommandHistory),NewCmdNum,ScreenInfo);
  4122. }
  4123. *CurrentCommand = COMMAND_NUM_TO_INDEX(NewCmdNum,CommandHistory);
  4124. }
  4125. PCOMMAND_HISTORY
  4126. FindCommandHistory(
  4127. IN PCONSOLE_INFORMATION Console,
  4128. IN HANDLE ProcessHandle
  4129. )
  4130. /*++
  4131. Routine Description:
  4132. This routine marks the command history buffer freed.
  4133. Arguments:
  4134. Console - pointer to console.
  4135. ProcessHandle - handle to client process.
  4136. Return Value:
  4137. none.
  4138. --*/
  4139. {
  4140. PCOMMAND_HISTORY History;
  4141. PLIST_ENTRY ListHead, ListNext;
  4142. ListHead = &Console->CommandHistoryList;
  4143. ListNext = ListHead->Flink;
  4144. while (ListNext != ListHead) {
  4145. History = CONTAINING_RECORD(ListNext, COMMAND_HISTORY, ListLink);
  4146. ListNext = ListNext->Flink;
  4147. if (History->ProcessHandle == ProcessHandle) {
  4148. UserAssert(History->Flags & CLE_ALLOCATED);
  4149. return History;
  4150. }
  4151. }
  4152. return NULL;
  4153. }
  4154. VOID
  4155. FreeCommandHistory(
  4156. IN PCONSOLE_INFORMATION Console,
  4157. IN HANDLE ProcessHandle
  4158. )
  4159. /*++
  4160. Routine Description:
  4161. This routine marks the command history buffer freed.
  4162. Arguments:
  4163. Console - pointer to console.
  4164. ProcessHandle - handle to client process.
  4165. Return Value:
  4166. none.
  4167. --*/
  4168. {
  4169. PCOMMAND_HISTORY History;
  4170. History = FindCommandHistory(Console,ProcessHandle);
  4171. if (History) {
  4172. History->Flags &= ~CLE_ALLOCATED;
  4173. History->ProcessHandle = NULL;
  4174. }
  4175. }
  4176. VOID
  4177. FreeCommandHistoryBuffers(
  4178. IN OUT PCONSOLE_INFORMATION Console
  4179. )
  4180. {
  4181. PCOMMAND_HISTORY History;
  4182. PLIST_ENTRY ListHead, ListNext;
  4183. SHORT i;
  4184. ListHead = &Console->CommandHistoryList;
  4185. ListNext = ListHead->Flink;
  4186. while (ListNext != ListHead) {
  4187. History = CONTAINING_RECORD( ListNext, COMMAND_HISTORY, ListLink );
  4188. ListNext = ListNext->Flink;
  4189. RemoveEntryList(&History->ListLink);
  4190. if (History->AppName) {
  4191. ConsoleHeapFree(History->AppName);
  4192. }
  4193. for (i=0;i<History->NumberOfCommands;i++) {
  4194. ConsoleHeapFree(History->Commands[i]);
  4195. }
  4196. ConsoleHeapFree(History);
  4197. }
  4198. }
  4199. VOID
  4200. ResizeCommandHistoryBuffers(
  4201. IN PCONSOLE_INFORMATION Console,
  4202. IN UINT NumCommands
  4203. )
  4204. {
  4205. PCOMMAND_HISTORY History;
  4206. PLIST_ENTRY ListHead, ListNext;
  4207. #if defined(FE_SB)
  4208. PCOOKED_READ_DATA CookedReadData;
  4209. PCOMMAND_HISTORY NewHistory;
  4210. #endif
  4211. UserAssert(NumCommands <= 0xFFFF);
  4212. Console->CommandHistorySize = (SHORT)NumCommands;
  4213. ListHead = &Console->CommandHistoryList;
  4214. ListNext = ListHead->Flink;
  4215. while (ListNext != ListHead) {
  4216. History = CONTAINING_RECORD( ListNext, COMMAND_HISTORY, ListLink );
  4217. ListNext = ListNext->Flink;
  4218. #if defined(FE_SB)
  4219. NewHistory = ReallocCommandHistory(Console, History, NumCommands);
  4220. CookedReadData = Console->lpCookedReadData;
  4221. if (CookedReadData && CookedReadData->CommandHistory == History) {
  4222. CookedReadData->CommandHistory = NewHistory;
  4223. }
  4224. #else
  4225. if (!(History->Flags & CLE_ALLOCATED)) {
  4226. ReallocCommandHistory(Console, History, NumCommands);
  4227. }
  4228. #endif
  4229. }
  4230. }
  4231. VOID
  4232. InitializeConsoleCommandData(
  4233. IN PCONSOLE_INFORMATION Console
  4234. )
  4235. /*++
  4236. Routine Description:
  4237. This routine initializes the per-console commandline recall data structures.
  4238. Arguments:
  4239. Console - pointer to console.
  4240. Return Value:
  4241. none
  4242. --*/
  4243. {
  4244. Console->NumCommandHistories = 0;
  4245. InitializeListHead(&Console->CommandHistoryList);
  4246. }
  4247. VOID
  4248. ResetCommandHistory(
  4249. IN PCOMMAND_HISTORY CommandHistory
  4250. )
  4251. /*++
  4252. This routine is called when escape is entered or a command is added.
  4253. --*/
  4254. {
  4255. if (CommandHistory == NULL) {
  4256. return;
  4257. }
  4258. CommandHistory->LastDisplayed = CommandHistory->LastAdded;
  4259. CommandHistory->Flags |= CLE_RESET;
  4260. }
  4261. NTSTATUS
  4262. AddCommand(
  4263. IN PCOMMAND_HISTORY CommandHistory,
  4264. IN PWCHAR Command,
  4265. IN USHORT Length,
  4266. IN BOOL HistoryNoDup
  4267. )
  4268. {
  4269. PCOMMAND *ppCmd;
  4270. if (CommandHistory == NULL || CommandHistory->MaximumNumberOfCommands == 0) {
  4271. return STATUS_NO_MEMORY;
  4272. }
  4273. UserAssert(CommandHistory->Flags & CLE_ALLOCATED);
  4274. if (Length == 0) {
  4275. return STATUS_SUCCESS;
  4276. }
  4277. if (CommandHistory->NumberOfCommands == 0 ||
  4278. CommandHistory->Commands[CommandHistory->LastAdded]->CommandLength != Length ||
  4279. memcmp(CommandHistory->Commands[CommandHistory->LastAdded]->Command,Command,Length)) {
  4280. PCOMMAND pCmdReuse = NULL;
  4281. if (HistoryNoDup) {
  4282. SHORT i;
  4283. i = FindMatchingCommand(CommandHistory, Command, Length,
  4284. CommandHistory->LastDisplayed, FMCFL_EXACT_MATCH);
  4285. if (i != -1) {
  4286. pCmdReuse = RemoveCommand(CommandHistory, i);
  4287. }
  4288. }
  4289. //
  4290. // find free record. if all records are used, free the lru one.
  4291. //
  4292. if (CommandHistory->NumberOfCommands < CommandHistory->MaximumNumberOfCommands) {
  4293. CommandHistory->LastAdded += 1;
  4294. CommandHistory->NumberOfCommands++;
  4295. } else {
  4296. COMMAND_IND_INC(CommandHistory->LastAdded, CommandHistory);
  4297. COMMAND_IND_INC(CommandHistory->FirstCommand, CommandHistory);
  4298. ConsoleHeapFree(CommandHistory->Commands[CommandHistory->LastAdded]);
  4299. if (CommandHistory->LastDisplayed == CommandHistory->LastAdded) {
  4300. CommandHistory->LastDisplayed = -1;
  4301. }
  4302. }
  4303. if (CommandHistory->LastDisplayed == -1 ||
  4304. CommandHistory->Commands[CommandHistory->LastDisplayed]->CommandLength != Length ||
  4305. memcmp(CommandHistory->Commands[CommandHistory->LastDisplayed]->Command,Command,Length)) {
  4306. ResetCommandHistory(CommandHistory);
  4307. }
  4308. //
  4309. // add command to array
  4310. //
  4311. ppCmd = &CommandHistory->Commands[CommandHistory->LastAdded];
  4312. if (pCmdReuse) {
  4313. *ppCmd = pCmdReuse;
  4314. } else {
  4315. *ppCmd = ConsoleHeapAlloc(HISTORY_TAG,
  4316. Length + sizeof(COMMAND));
  4317. if (*ppCmd == NULL) {
  4318. COMMAND_IND_PREV(CommandHistory->LastAdded, CommandHistory);
  4319. CommandHistory->NumberOfCommands -= 1;
  4320. return STATUS_NO_MEMORY;
  4321. }
  4322. (*ppCmd)->CommandLength = Length;
  4323. RtlCopyMemory((*ppCmd)->Command,Command,Length);
  4324. }
  4325. }
  4326. CommandHistory->Flags |= CLE_RESET; // remember that we've returned a cmd
  4327. return STATUS_SUCCESS;
  4328. }
  4329. NTSTATUS
  4330. RetrieveCommand(
  4331. IN PCOMMAND_HISTORY CommandHistory,
  4332. IN WORD VirtualKeyCode,
  4333. IN PWCHAR Buffer,
  4334. IN ULONG BufferSize,
  4335. OUT PULONG CommandSize)
  4336. {
  4337. if (CommandHistory == NULL) {
  4338. return STATUS_UNSUCCESSFUL;
  4339. }
  4340. UserAssert(CommandHistory->Flags & CLE_ALLOCATED);
  4341. if (CommandHistory->NumberOfCommands == 0) {
  4342. return STATUS_UNSUCCESSFUL;
  4343. }
  4344. if (CommandHistory->NumberOfCommands == 1) {
  4345. CommandHistory->LastDisplayed = 0;
  4346. } else if (VirtualKeyCode == VK_UP) {
  4347. //
  4348. // if this is the first time for this read that a command has
  4349. // been retrieved, return the current command. otherwise, return
  4350. // the previous command.
  4351. //
  4352. if (CommandHistory->Flags & CLE_RESET) {
  4353. CommandHistory->Flags &= ~CLE_RESET;
  4354. } else {
  4355. COMMAND_IND_PREV(CommandHistory->LastDisplayed, CommandHistory);
  4356. }
  4357. } else {
  4358. COMMAND_IND_NEXT(CommandHistory->LastDisplayed, CommandHistory);
  4359. }
  4360. return RetrieveNthCommand(CommandHistory,
  4361. CommandHistory->LastDisplayed,
  4362. Buffer,
  4363. BufferSize,
  4364. CommandSize
  4365. );
  4366. }
  4367. ULONG
  4368. SrvGetConsoleTitle(
  4369. IN OUT PCSR_API_MSG m,
  4370. IN OUT PCSR_REPLY_STATUS ReplyStatus
  4371. )
  4372. {
  4373. PCONSOLE_GETTITLE_MSG a = (PCONSOLE_GETTITLE_MSG)&m->u.ApiMessageData;
  4374. NTSTATUS Status;
  4375. PCONSOLE_INFORMATION Console;
  4376. Status = ApiPreamble(a->ConsoleHandle,
  4377. &Console
  4378. );
  4379. if (!NT_SUCCESS(Status)) {
  4380. return Status;
  4381. }
  4382. if (!CsrValidateMessageBuffer(m, &a->Title, a->TitleLength, sizeof(BYTE))) {
  4383. UnlockConsole(Console);
  4384. return STATUS_INVALID_PARAMETER;
  4385. }
  4386. // a->TitleLength contains length in bytes
  4387. if (a->Unicode) {
  4388. if ((USHORT)a->TitleLength > Console->TitleLength) {
  4389. a->TitleLength = Console->TitleLength;
  4390. }
  4391. RtlCopyMemory(a->Title,Console->Title,a->TitleLength);
  4392. } else {
  4393. #if defined(FE_SB)
  4394. a->TitleLength = (USHORT)ConvertToOem(OEMCP,
  4395. Console->Title,
  4396. Console->TitleLength / sizeof(WCHAR),
  4397. a->Title,
  4398. a->TitleLength
  4399. );
  4400. #else
  4401. a->TitleLength = (USHORT)ConvertToOem(Console->CP,
  4402. Console->Title,
  4403. Console->TitleLength / sizeof(WCHAR),
  4404. a->Title,
  4405. a->TitleLength
  4406. );
  4407. #endif
  4408. }
  4409. UnlockConsole(Console);
  4410. return STATUS_SUCCESS;
  4411. UNREFERENCED_PARAMETER(ReplyStatus); // get rid of unreferenced parameter warning message
  4412. }
  4413. ULONG
  4414. SrvSetConsoleTitle(
  4415. IN OUT PCSR_API_MSG m,
  4416. IN OUT PCSR_REPLY_STATUS ReplyStatus
  4417. )
  4418. {
  4419. PCONSOLE_SETTITLE_MSG a = (PCONSOLE_SETTITLE_MSG)&m->u.ApiMessageData;
  4420. NTSTATUS Status;
  4421. PCONSOLE_INFORMATION Console;
  4422. LPWSTR NewTitle;
  4423. Status = ApiPreamble(a->ConsoleHandle,
  4424. &Console
  4425. );
  4426. if (!NT_SUCCESS(Status)) {
  4427. return Status;
  4428. }
  4429. if (!CsrValidateMessageBuffer(m, &a->Title, a->TitleLength, sizeof(BYTE))) {
  4430. UnlockConsole(Console);
  4431. return STATUS_INVALID_PARAMETER;
  4432. }
  4433. if (!a->Unicode) {
  4434. NewTitle = ConsoleHeapAlloc(TITLE_TAG,
  4435. a->TitleLength * sizeof(WCHAR) + sizeof(WCHAR));
  4436. if (NewTitle == NULL) {
  4437. UnlockConsole(Console);
  4438. return STATUS_NO_MEMORY;
  4439. }
  4440. // convert title to unicode
  4441. #if defined(FE_SB)
  4442. Console->TitleLength = (USHORT)ConvertInputToUnicode(OEMCP,
  4443. a->Title,
  4444. a->TitleLength,
  4445. NewTitle,
  4446. a->TitleLength);
  4447. #else
  4448. Console->TitleLength = (USHORT)ConvertInputToUnicode(Console->CP,
  4449. a->Title,
  4450. a->TitleLength,
  4451. NewTitle,
  4452. a->TitleLength);
  4453. #endif
  4454. Console->TitleLength *= 2;
  4455. } else {
  4456. // a->TitleLength contains length in bytes
  4457. NewTitle = ConsoleHeapAlloc(TITLE_TAG, a->TitleLength + sizeof(WCHAR));
  4458. if (NewTitle == NULL) {
  4459. UnlockConsole(Console);
  4460. return STATUS_NO_MEMORY;
  4461. }
  4462. Console->TitleLength = (USHORT)a->TitleLength;
  4463. RtlCopyMemory(NewTitle,a->Title,a->TitleLength);
  4464. }
  4465. NewTitle[Console->TitleLength/sizeof(WCHAR)] = 0; // NULL terminate
  4466. ConsoleHeapFree(Console->Title);
  4467. Console->Title = NewTitle;
  4468. PostMessage(Console->hWnd, CM_UPDATE_TITLE, 0, 0);
  4469. UnlockConsole(Console);
  4470. return STATUS_SUCCESS;
  4471. UNREFERENCED_PARAMETER(ReplyStatus); // get rid of unreferenced parameter warning message
  4472. }
  4473. int
  4474. LoadStringExW(
  4475. IN HINSTANCE hModule,
  4476. IN UINT wID,
  4477. OUT LPWSTR lpBuffer,
  4478. IN int cchBufferMax,
  4479. IN WORD wLangId
  4480. )
  4481. {
  4482. HANDLE hResInfo;
  4483. HANDLE hStringSeg;
  4484. LPTSTR lpsz;
  4485. int cch;
  4486. /*
  4487. * Make sure the parms are valid.
  4488. */
  4489. if (lpBuffer == NULL) {
  4490. return 0;
  4491. }
  4492. cch = 0;
  4493. /*
  4494. * String Tables are broken up into 16 string segments. Find the segment
  4495. * containing the string we are interested in.
  4496. */
  4497. if (hResInfo = FindResourceEx(hModule,
  4498. RT_STRING,
  4499. (LPTSTR)((LONG_PTR)(((USHORT)wID >> 4) + 1)),
  4500. wLangId)) {
  4501. /*
  4502. * Load that segment.
  4503. */
  4504. hStringSeg = LoadResource(hModule, hResInfo);
  4505. /*
  4506. * Lock the resource.
  4507. */
  4508. if (lpsz = (LPTSTR)LockResource(hStringSeg)) {
  4509. /*
  4510. * Move past the other strings in this segment.
  4511. * (16 strings in a segment -> & 0x0F)
  4512. */
  4513. wID &= 0x0F;
  4514. while (TRUE) {
  4515. cch = *((UTCHAR *)lpsz++); // PASCAL like string count
  4516. // first UTCHAR is count if TCHARs
  4517. if (wID-- == 0) break;
  4518. lpsz += cch; // Step to start if next string
  4519. }
  4520. /*
  4521. * chhBufferMax == 0 means return a pointer to the read-only resource buffer.
  4522. */
  4523. if (cchBufferMax == 0) {
  4524. *(LPTSTR *)lpBuffer = lpsz;
  4525. } else {
  4526. /*
  4527. * Account for the NULL
  4528. */
  4529. cchBufferMax--;
  4530. /*
  4531. * Don't copy more than the max allowed.
  4532. */
  4533. if (cch > cchBufferMax)
  4534. cch = cchBufferMax;
  4535. /*
  4536. * Copy the string into the buffer.
  4537. */
  4538. RtlCopyMemory(lpBuffer, lpsz, cch*sizeof(WCHAR));
  4539. }
  4540. }
  4541. }
  4542. /*
  4543. * Append a NULL.
  4544. */
  4545. if (cchBufferMax != 0) {
  4546. lpBuffer[cch] = 0;
  4547. }
  4548. return cch;
  4549. }