term.c 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982
  1. /*++
  2. Copyright (c) 2014 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. term.c
  5. Abstract:
  6. This module implements common terminal support. It understands roughly the
  7. VT220 terminal command set, with some xterm support in there too.
  8. Author:
  9. Evan Green 24-Jul-2014
  10. Environment:
  11. Kernel
  12. --*/
  13. //
  14. // ------------------------------------------------------------------- Includes
  15. //
  16. #define RTL_API
  17. #include <minoca/lib/types.h>
  18. #include <minoca/lib/status.h>
  19. #include <minoca/lib/rtl.h>
  20. #include <minoca/lib/termlib.h>
  21. //
  22. // ---------------------------------------------------------------- Definitions
  23. //
  24. //
  25. // ------------------------------------------------------ Data Type Definitions
  26. //
  27. /*++
  28. Structure Description:
  29. This structure stores the decoding information for a terminal escape
  30. sequence.
  31. Members:
  32. PreParameterString - Stores an optional pointer to the sequence of
  33. characters occurring before the parameter.
  34. PostParameterString - Stores the sequence of strings occurring after the
  35. parameters.
  36. Command - Stores the resulting command.
  37. --*/
  38. typedef struct _TERMINAL_DECODE_ENTRY {
  39. PSTR PreParameterString;
  40. PSTR PostParameterString;
  41. TERMINAL_COMMAND Command;
  42. } TERMINAL_DECODE_ENTRY, *PTERMINAL_DECODE_ENTRY;
  43. /*++
  44. Structure Description:
  45. This structure stores the decoding information for a terminal escape
  46. sequence.
  47. Members:
  48. Sequence - Supplies a pointer to a string containing the escape sequence
  49. (after the escape) corresponding to this key.
  50. ApplicationMode - Supplies a boolean indicating if this key represents
  51. application mode keys or normal mode keys.
  52. Key - Supplies the corresponding key code for this sequence.
  53. --*/
  54. typedef struct _TERMINAL_KEY_ENTRY {
  55. PSTR Sequence;
  56. BOOL ApplicationMode;
  57. TERMINAL_KEY Key;
  58. } TERMINAL_KEY_ENTRY, *PTERMINAL_KEY_ENTRY;
  59. //
  60. // ----------------------------------------------- Internal Function Prototypes
  61. //
  62. BOOL
  63. TermpMatchCommand (
  64. PSTR PreString,
  65. UINTN PreStringSize,
  66. PSTR PostString,
  67. UINTN PostStringSize,
  68. PTERMINAL_DECODE_ENTRY DecodeEntry,
  69. PBOOL PartialMatch
  70. );
  71. //
  72. // -------------------------------------------------------------------- Globals
  73. //
  74. TERMINAL_DECODE_ENTRY TermCommandTable[] = {
  75. {"[", "A", TerminalCommandCursorUp},
  76. {"[", "B", TerminalCommandCursorDown},
  77. {"[", "C", TerminalCommandCursorRight},
  78. {"[", "D", TerminalCommandCursorLeft},
  79. {"[", "f", TerminalCommandCursorMove},
  80. {"[", "H", TerminalCommandCursorMove},
  81. {"[", "d", TerminalCommandSetCursorRowAbsolute},
  82. {"[", "e", TerminalCommandCursorDown},
  83. {"[", "G", TerminalCommandSetCursorColumnAbsolute},
  84. {"", "c", TerminalCommandReset},
  85. {"", "D", TerminalCommandCursorDown},
  86. {"", "E", TerminalCommandNextLine},
  87. {"", "M", TerminalCommandReverseLineFeed},
  88. {"", "7", TerminalCommandSaveCursorAndAttributes},
  89. {"", "8", TerminalCommandRestoreCursorAndAttributes},
  90. {"", "H", TerminalCommandSetHorizontalTab},
  91. {"[", "g", TerminalCommandClearHorizontalTab},
  92. {"[", "r", TerminalCommandSetTopAndBottomMargin},
  93. {"[", "J", TerminalCommandEraseInDisplay},
  94. {"[?", "J", TerminalCommandEraseInDisplaySelective},
  95. {"[", "K", TerminalCommandEraseInLine},
  96. {"[?", "K", TerminalCommandEraseInLineSelective},
  97. {"[", "L", TerminalCommandInsertLines},
  98. {"[", "M", TerminalCommandDeleteLines},
  99. {"[", "@", TerminalCommandInsertCharacters},
  100. {"[", "P", TerminalCommandDeleteCharacters},
  101. {"[", "X", TerminalCommandEraseCharacters},
  102. {"", ">", TerminalCommandKeypadNumeric},
  103. {"", "=", TerminalCommandKeypadApplication},
  104. {"[", "l", TerminalCommandClearMode},
  105. {"[", "h", TerminalCommandSetMode},
  106. {"[?", "l", TerminalCommandClearPrivateMode},
  107. {"[?", "h", TerminalCommandSetPrivateMode},
  108. {"(", "", TerminalCommandSelectG0CharacterSet},
  109. {")", "", TerminalCommandSelectG1CharacterSet},
  110. {"*", "", TerminalCommandSelectG2CharacterSet},
  111. {"+", "", TerminalCommandSelectG3CharacterSet},
  112. {"[", "m", TerminalCommandSelectGraphicRendition},
  113. {"", "c", TerminalCommandReset},
  114. {"[", "!p", TerminalCommandSoftReset},
  115. {"[", "c", TerminalCommandDeviceAttributesPrimary},
  116. {"[", ">c", TerminalCommandDeviceAttributesSecondary},
  117. {"[", "S", TerminalCommandScrollUp},
  118. {"[", "T", TerminalCommandScrollDown},
  119. {"#", "3", TerminalCommandDoubleLineHeightTopHalf},
  120. {"#", "4", TerminalCommandDoubleLineHeightBottomHalf},
  121. {"#", "5", TerminalCommandSingleWidthLine},
  122. {"#", "6", TerminalCommandDoubleWidthLine},
  123. };
  124. TERMINAL_KEY_ENTRY TermKeyTable[] = {
  125. {"[A", FALSE, TerminalKeyUp},
  126. {"[B", FALSE, TerminalKeyDown},
  127. {"[C", FALSE, TerminalKeyRight},
  128. {"[D", FALSE, TerminalKeyLeft},
  129. {"[2~", FALSE, TerminalKeyInsert},
  130. {"[3~", FALSE, TerminalKeyDelete},
  131. {"[1~", FALSE, TerminalKeyHome},
  132. {"[4~", FALSE, TerminalKeyEnd},
  133. {"[5~", FALSE, TerminalKeyPageUp},
  134. {"[6~", FALSE, TerminalKeyPageDown},
  135. };
  136. //
  137. // ------------------------------------------------------------------ Functions
  138. //
  139. TERMINAL_PARSE_RESULT
  140. TermProcessOutput (
  141. PTERMINAL_COMMAND_DATA Command,
  142. CHAR Character
  143. )
  144. /*++
  145. Routine Description:
  146. This routine processes a character destined for the terminal output.
  147. Arguments:
  148. Command - Supplies a pointer to the current command state. If this is the
  149. first character ever, zero out the command before calling this function.
  150. Character - Supplies the input character.
  151. Return Value:
  152. Returns a terminal output result code indicating if the character is just a
  153. normal display character, part of a command, or the last character in a
  154. complete command.
  155. --*/
  156. {
  157. UINTN CommandCount;
  158. UINTN CommandIndex;
  159. PTERMINAL_DECODE_ENTRY DecodeEntry;
  160. BOOL Match;
  161. UINTN ParameterIndex;
  162. BOOL PartialMatch;
  163. //
  164. // An escape character always starts a new command.
  165. //
  166. if (Character == TERMINAL_ESCAPE) {
  167. Command->Flags = TERMINAL_COMMAND_SEEN_ESCAPE;
  168. Command->CommandCharacterCount = 0;
  169. Command->ParameterCount = 0;
  170. Command->ParameterIndex = 0;
  171. Command->Parameter[0] = 0;
  172. Command->PreParameterSize = 0;
  173. Command->PostParameterSize = 0;
  174. Command->Command = TerminalCommandInvalid;
  175. return TerminalParseResultPartialCommand;
  176. }
  177. //
  178. // If an escape hasn't been seen then this is just an ordinary character.
  179. //
  180. if ((Command->Flags & TERMINAL_COMMAND_SEEN_ESCAPE) == 0) {
  181. return TerminalParseResultNormalCharacter;
  182. }
  183. //
  184. // If it's a control character, return it as normal.
  185. //
  186. if ((Character < ' ') || (Character > 0x7F)) {
  187. return TerminalParseResultNormalCharacter;
  188. }
  189. //
  190. // If this is a digit, then it's either a parameter for a CSI (^[) sequence
  191. // or it's a command of its own (like ^7 or ^8). If a CSI has been seen,
  192. // treat it as a parameter, otherwise, treat it like a command character.
  193. //
  194. if ((Character >= '0') && (Character <= '9')) {
  195. if ((Command->PreParameterSize != 0) &&
  196. (Command->PreParameter[0] == TERMINAL_INTRODUCER)) {
  197. Command->Flags |= TERMINAL_COMMAND_SEEN_PARAMETER;
  198. ParameterIndex = Command->ParameterIndex;
  199. //
  200. // If this is the first time a digit for a parameter is specified,
  201. // then bump up the parameter count. Watch out for too many
  202. // parameters.
  203. //
  204. if (Command->ParameterCount < ParameterIndex + 1) {
  205. if (ParameterIndex >= TERMINAL_MAX_PARAMETERS) {
  206. Command->Flags = 0;
  207. return TerminalParseResultNormalCharacter;
  208. }
  209. Command->ParameterCount = ParameterIndex + 1;
  210. Command->Parameter[ParameterIndex] = 0;
  211. }
  212. Command->Parameter[ParameterIndex] *= 10;
  213. Command->Parameter[ParameterIndex] += Character - '0';
  214. return TerminalParseResultPartialCommand;
  215. }
  216. //
  217. // Move to the next parameter slot.
  218. //
  219. } else if (Character == TERMINAL_PARAMETER_SEPARATOR) {
  220. Command->ParameterIndex += 1;
  221. if (Command->ParameterIndex < TERMINAL_MAX_PARAMETERS) {
  222. Command->Parameter[Command->ParameterIndex] = 0;
  223. }
  224. return TerminalParseResultPartialCommand;
  225. }
  226. //
  227. // If the character was not a parameter, then add it to the command buffer.
  228. // Add it to the beginning or end depending on whether or not a parameter
  229. // was seen.
  230. //
  231. if ((Command->Flags & TERMINAL_COMMAND_SEEN_PARAMETER) != 0) {
  232. if (Command->PostParameterSize >= TERMINAL_MAX_COMMAND_CHARACTERS) {
  233. Command->Flags = 0;
  234. return TerminalParseResultNormalCharacter;
  235. }
  236. Command->PostParameter[Command->PostParameterSize] = Character;
  237. Command->PostParameterSize += 1;
  238. } else {
  239. if (Command->PreParameterSize >= TERMINAL_MAX_COMMAND_CHARACTERS) {
  240. Command->Flags = 0;
  241. return TerminalParseResultNormalCharacter;
  242. }
  243. Command->PreParameter[Command->PreParameterSize] = Character;
  244. Command->PreParameterSize += 1;
  245. }
  246. //
  247. // As a shortcut to prevent the following loop in common cases, skip the
  248. // test if this is the introducer.
  249. //
  250. if (Character == TERMINAL_INTRODUCER) {
  251. return TerminalParseResultPartialCommand;
  252. }
  253. //
  254. // Look to see if the command matches anything completely or partially.
  255. //
  256. PartialMatch = FALSE;
  257. CommandCount = sizeof(TermCommandTable) / sizeof(TermCommandTable[0]);
  258. for (CommandIndex = 0; CommandIndex < CommandCount; CommandIndex += 1) {
  259. DecodeEntry = &(TermCommandTable[CommandIndex]);
  260. Match = TermpMatchCommand(Command->PreParameter,
  261. Command->PreParameterSize,
  262. Command->PostParameter,
  263. Command->PostParameterSize,
  264. DecodeEntry,
  265. &PartialMatch);
  266. if (Match != FALSE) {
  267. break;
  268. }
  269. //
  270. // If there is no post parameter and the decode entry's pre-parameter
  271. // string is empty, try matching the pre-parameter string against the
  272. // post-parameter decode entry string.
  273. //
  274. if ((DecodeEntry->PreParameterString[0] == '\0') &&
  275. (Command->PostParameterSize == 0)) {
  276. Match = TermpMatchCommand(NULL,
  277. 0,
  278. Command->PreParameter,
  279. Command->PreParameterSize,
  280. DecodeEntry,
  281. &PartialMatch);
  282. if (Match != FALSE) {
  283. break;
  284. }
  285. }
  286. }
  287. //
  288. // If the loop made it to the end, then no command matched exactly.
  289. //
  290. if (CommandIndex == CommandCount) {
  291. if (PartialMatch != FALSE) {
  292. return TerminalParseResultPartialCommand;
  293. }
  294. Command->Flags = 0;
  295. return TerminalParseResultNormalCharacter;
  296. }
  297. Command->Command = DecodeEntry->Command;
  298. Command->Flags = 0;
  299. return TerminalParseResultCompleteCommand;
  300. }
  301. VOID
  302. TermNormalizeParameters (
  303. PTERMINAL_COMMAND_DATA Command
  304. )
  305. /*++
  306. Routine Description:
  307. This routine normalizes the command parameters to their expected defaults
  308. and allowed.
  309. Arguments:
  310. Command - Supplies a pointer to the complete command.
  311. Return Value:
  312. None.
  313. --*/
  314. {
  315. UINTN Index;
  316. switch (Command->Command) {
  317. case TerminalCommandCursorUp:
  318. case TerminalCommandCursorDown:
  319. case TerminalCommandCursorLeft:
  320. case TerminalCommandCursorRight:
  321. case TerminalCommandScrollUp:
  322. case TerminalCommandScrollDown:
  323. case TerminalCommandSetCursorRowAbsolute:
  324. case TerminalCommandSetCursorColumnAbsolute:
  325. if (Command->ParameterCount == 0) {
  326. Command->Parameter[0] = 1;
  327. }
  328. Command->ParameterCount = 1;
  329. if (Command->Parameter[0] == 0) {
  330. Command->Parameter[0] = 1;
  331. }
  332. break;
  333. case TerminalCommandCursorMove:
  334. for (Index = 0; Index < 2; Index += 1) {
  335. if (Index >= Command->ParameterCount) {
  336. Command->Parameter[Index] = 1;
  337. } else if (Command->Parameter[Index] == 0) {
  338. Command->Parameter[Index] = 1;
  339. }
  340. }
  341. Command->ParameterCount = 2;
  342. break;
  343. case TerminalCommandNextLine:
  344. case TerminalCommandReverseLineFeed:
  345. case TerminalCommandSaveCursorAndAttributes:
  346. case TerminalCommandRestoreCursorAndAttributes:
  347. case TerminalCommandSetHorizontalTab:
  348. case TerminalCommandKeypadNumeric:
  349. case TerminalCommandKeypadApplication:
  350. case TerminalCommandReset:
  351. case TerminalCommandSoftReset:
  352. case TerminalCommandDeviceAttributesPrimary:
  353. case TerminalCommandDeviceAttributesSecondary:
  354. case TerminalCommandDoubleLineHeightTopHalf:
  355. case TerminalCommandDoubleLineHeightBottomHalf:
  356. case TerminalCommandSingleWidthLine:
  357. case TerminalCommandDoubleWidthLine:
  358. Command->ParameterCount = 0;
  359. break;
  360. case TerminalCommandClearHorizontalTab:
  361. case TerminalCommandEraseInDisplay:
  362. case TerminalCommandEraseInLine:
  363. if (Command->ParameterCount == 0) {
  364. Command->Parameter[0] = 0;
  365. }
  366. Command->ParameterCount = 1;
  367. break;
  368. case TerminalCommandInsertLines:
  369. case TerminalCommandDeleteLines:
  370. case TerminalCommandInsertCharacters:
  371. case TerminalCommandDeleteCharacters:
  372. case TerminalCommandEraseCharacters:
  373. if (Command->ParameterCount == 0) {
  374. Command->Parameter[0] = 1;
  375. }
  376. Command->ParameterCount = 1;
  377. break;
  378. case TerminalCommandSetTopAndBottomMargin:
  379. case TerminalCommandSetMode:
  380. case TerminalCommandClearMode:
  381. case TerminalCommandSelectG0CharacterSet:
  382. case TerminalCommandSelectG1CharacterSet:
  383. case TerminalCommandSelectG2CharacterSet:
  384. case TerminalCommandSelectG3CharacterSet:
  385. case TerminalCommandSelectGraphicRendition:
  386. default:
  387. break;
  388. }
  389. return;
  390. }
  391. BOOL
  392. TermCreateOutputSequence (
  393. PTERMINAL_COMMAND_DATA Command,
  394. PSTR Buffer,
  395. UINTN BufferSize
  396. )
  397. /*++
  398. Routine Description:
  399. This routine creates a terminal command sequence for a given command.
  400. Arguments:
  401. Command - Supplies a pointer to the complete command.
  402. Buffer - Supplies a pointer where the null-terminated command sequence will
  403. be returned.
  404. BufferSize - Supplies the size of the supplied buffer in bytes.
  405. Return Value:
  406. TRUE on success.
  407. FALSE on failure.
  408. --*/
  409. {
  410. PTERMINAL_DECODE_ENTRY DecodeEntry;
  411. UINTN EntryCount;
  412. UINTN EntryIndex;
  413. UINTN FinalLength;
  414. PSTR Format;
  415. UINTN ParameterIndex;
  416. DecodeEntry = NULL;
  417. EntryCount = sizeof(TermCommandTable) / sizeof(TermCommandTable[0]);
  418. for (EntryIndex = 0; EntryIndex < EntryCount; EntryIndex += 1) {
  419. DecodeEntry = &(TermCommandTable[EntryIndex]);
  420. if (DecodeEntry->Command == Command->Command) {
  421. break;
  422. }
  423. }
  424. if (EntryIndex == EntryCount) {
  425. return FALSE;
  426. }
  427. //
  428. // If the post parameter string is NULL, then the final sequence is a
  429. // single character.
  430. //
  431. if (DecodeEntry->PostParameterString[0] == '\0') {
  432. ASSERT(Command->PostParameterSize == 1);
  433. FinalLength = RtlPrintToString(Buffer,
  434. BufferSize,
  435. CharacterEncodingAscii,
  436. "%c%s%c",
  437. TERMINAL_ESCAPE,
  438. DecodeEntry->PreParameterString,
  439. Command->PostParameter[0]);
  440. if (FinalLength > BufferSize) {
  441. return FALSE;
  442. }
  443. //
  444. // Output the format ^<prestring><parameters><poststring>, where ^ is the
  445. // escape character (0x1B), and parameters are a sequence of
  446. // <number>;...;<number>.
  447. //
  448. } else {
  449. FinalLength = RtlPrintToString(Buffer,
  450. BufferSize,
  451. CharacterEncodingAscii,
  452. "%c%s",
  453. TERMINAL_ESCAPE,
  454. DecodeEntry->PreParameterString);
  455. if (FinalLength >= BufferSize) {
  456. return FALSE;
  457. }
  458. Buffer += FinalLength - 1;
  459. BufferSize -= FinalLength - 1;
  460. for (ParameterIndex = 0;
  461. ParameterIndex < Command->ParameterCount;
  462. ParameterIndex += 1) {
  463. if (ParameterIndex == Command->ParameterCount - 1) {
  464. Format = "%d";
  465. } else {
  466. Format = "%d;";
  467. }
  468. FinalLength = RtlPrintToString(Buffer,
  469. BufferSize,
  470. CharacterEncodingAscii,
  471. Format,
  472. Command->Parameter[ParameterIndex]);
  473. if (FinalLength >= BufferSize) {
  474. return FALSE;
  475. }
  476. Buffer += FinalLength - 1;
  477. BufferSize -= FinalLength - 1;
  478. }
  479. RtlPrintToString(Buffer,
  480. BufferSize,
  481. CharacterEncodingAscii,
  482. "%s",
  483. DecodeEntry->PostParameterString);
  484. }
  485. return TRUE;
  486. }
  487. TERMINAL_PARSE_RESULT
  488. TermProcessInput (
  489. PTERMINAL_KEY_DATA KeyData,
  490. CHAR Character
  491. )
  492. /*++
  493. Routine Description:
  494. This routine processes a character destined for the terminal input.
  495. Arguments:
  496. KeyData - Supplies a pointer to the key parsing state. If this is the first
  497. time calling this function, zero out this structure.
  498. Character - Supplies the input character.
  499. Return Value:
  500. Returns a terminal parse result code indicating if the character is just a
  501. normal input character, part of a command, or the last character in a
  502. complete command.
  503. --*/
  504. {
  505. UINTN CharacterIndex;
  506. UINTN DecodeCount;
  507. PTERMINAL_KEY_ENTRY DecodeEntry;
  508. UINTN DecodeIndex;
  509. BOOL PartialMatch;
  510. //
  511. // An escape character always starts a new command.
  512. //
  513. if (Character == TERMINAL_ESCAPE) {
  514. //
  515. // Two escapes in a row means ALT was held down here.
  516. //
  517. if ((KeyData->Buffer[0] == TERMINAL_ESCAPE) &&
  518. (KeyData->BufferSize == 1)) {
  519. KeyData->Flags |= TERMINAL_KEY_FLAG_ALT;
  520. return TerminalParseResultPartialCommand;
  521. }
  522. KeyData->Buffer[0] = Character;
  523. KeyData->BufferSize = 1;
  524. KeyData->Flags = 0;
  525. return TerminalParseResultPartialCommand;
  526. }
  527. if (KeyData->BufferSize == 0) {
  528. return TerminalParseResultNormalCharacter;
  529. }
  530. if (KeyData->BufferSize == TERMINAL_MAX_KEY_CHARACTERS) {
  531. ASSERT(FALSE);
  532. KeyData->BufferSize = 0;
  533. return TerminalParseResultNormalCharacter;
  534. }
  535. KeyData->Buffer[KeyData->BufferSize] = Character;
  536. KeyData->BufferSize += 1;
  537. PartialMatch = FALSE;
  538. DecodeCount = sizeof(TermKeyTable) / sizeof(TermKeyTable[0]);
  539. for (DecodeIndex = 0; DecodeIndex < DecodeCount; DecodeIndex += 1) {
  540. DecodeEntry = &(TermKeyTable[DecodeIndex]);
  541. for (CharacterIndex = 0;
  542. CharacterIndex < KeyData->BufferSize - 1;
  543. CharacterIndex += 1) {
  544. if (DecodeEntry->Sequence[CharacterIndex] == '\0') {
  545. break;
  546. }
  547. if (DecodeEntry->Sequence[CharacterIndex] !=
  548. KeyData->Buffer[CharacterIndex + 1]) {
  549. break;
  550. }
  551. }
  552. //
  553. // If not all the input characters were processed, this doesn't match.
  554. //
  555. if (CharacterIndex != KeyData->BufferSize - 1) {
  556. continue;
  557. }
  558. //
  559. // If everything matched but the sequence isn't finished, this is a
  560. // partial match.
  561. //
  562. if (DecodeEntry->Sequence[CharacterIndex] != '\0') {
  563. PartialMatch = TRUE;
  564. continue;
  565. }
  566. //
  567. // Everything matches, this is the key.
  568. //
  569. break;
  570. }
  571. if (DecodeIndex == DecodeCount) {
  572. if (PartialMatch != FALSE) {
  573. return TerminalParseResultPartialCommand;
  574. }
  575. KeyData->BufferSize = 0;
  576. return TerminalParseResultNormalCharacter;
  577. }
  578. KeyData->Key = DecodeEntry->Key;
  579. KeyData->BufferSize = 0;
  580. return TerminalParseResultCompleteCommand;
  581. }
  582. BOOL
  583. TermCreateInputSequence (
  584. PTERMINAL_KEY_DATA KeyData,
  585. PSTR Buffer,
  586. UINTN BufferSize
  587. )
  588. /*++
  589. Routine Description:
  590. This routine creates a terminal keyboard sequence for a given key.
  591. Arguments:
  592. KeyData - Supplies the complete key data.
  593. Buffer - Supplies a pointer where the null-terminated control sequence will
  594. be returned.
  595. BufferSize - Supplies the size of the supplied buffer in bytes.
  596. Return Value:
  597. TRUE on success.
  598. FALSE on failure.
  599. --*/
  600. {
  601. UINTN DecodeCount;
  602. PTERMINAL_KEY_ENTRY DecodeEntry;
  603. UINTN DecodeIndex;
  604. DecodeCount = sizeof(TermKeyTable) / sizeof(TermKeyTable[0]);
  605. for (DecodeIndex = 0; DecodeIndex < DecodeCount; DecodeIndex += 1) {
  606. DecodeEntry = &(TermKeyTable[DecodeIndex]);
  607. if (DecodeEntry->Key == KeyData->Key) {
  608. break;
  609. }
  610. }
  611. if (DecodeIndex == DecodeCount) {
  612. return FALSE;
  613. }
  614. if (BufferSize == 0) {
  615. return FALSE;
  616. }
  617. //
  618. // Stick an extra escape on the front if the ALT flag is set.
  619. //
  620. if ((KeyData->Flags & TERMINAL_KEY_FLAG_ALT) != 0) {
  621. *Buffer = TERMINAL_ESCAPE;
  622. Buffer += 1;
  623. BufferSize -= 1;
  624. }
  625. if (BufferSize == 0) {
  626. return FALSE;
  627. }
  628. *Buffer = TERMINAL_ESCAPE;
  629. Buffer += 1;
  630. BufferSize -= 1;
  631. RtlStringCopy(Buffer, DecodeEntry->Sequence, BufferSize);
  632. return TRUE;
  633. }
  634. //
  635. // --------------------------------------------------------- Internal Functions
  636. //
  637. BOOL
  638. TermpMatchCommand (
  639. PSTR PreString,
  640. UINTN PreStringSize,
  641. PSTR PostString,
  642. UINTN PostStringSize,
  643. PTERMINAL_DECODE_ENTRY DecodeEntry,
  644. PBOOL PartialMatch
  645. )
  646. /*++
  647. Routine Description:
  648. This routine attempts to match the current input characters with the given
  649. command.
  650. Arguments:
  651. PreString - Supplies a pointer to the pre-parameter characters seen so far.
  652. PreStringSize - Supplies the size of the pre-parameter string in bytes.
  653. PostString - Supplies a pointer to the post-parameter characters seen so
  654. far.
  655. PostStringSize - Supplies the size of the post parameter string in bytes.
  656. DecodeEntry - Supplies a pointer to the decode entry to match against.
  657. PartialMatch - Supplies a pointer that is left alone if the entry matches
  658. or does not match, and is set to TRUE if the entry partially matches
  659. but needs more characters to fully match.
  660. Return Value:
  661. TRUE if the input matches the decode entry fully.
  662. FALSE if the input does not match or only partially matches the decode
  663. entry.
  664. --*/
  665. {
  666. PSTR PreStringTail;
  667. UINTN PreStringTailSize;
  668. UINTN StringIndex;
  669. //
  670. // Match the pre-parameter string.
  671. //
  672. for (StringIndex = 0; StringIndex < PreStringSize; StringIndex += 1) {
  673. if ((DecodeEntry->PreParameterString[StringIndex] == '\0') ||
  674. (DecodeEntry->PreParameterString[StringIndex] !=
  675. PreString[StringIndex])) {
  676. break;
  677. }
  678. }
  679. if (StringIndex != PreStringSize) {
  680. //
  681. // In the case where there were no parameters, the final character
  682. // may have been glommed on to the pre-parameter string. Try to
  683. // match the rest of the string with the post parameter string.
  684. //
  685. if ((DecodeEntry->PreParameterString[StringIndex] == 0) &&
  686. (PostStringSize == 0)) {
  687. //
  688. // If the post parameter string is empty, then any character
  689. // matches. The "Select Character Set" commands have a form like
  690. // this: ^({final}, where {final} is the desired hard character set.
  691. //
  692. if (DecodeEntry->PostParameterString[0] == '\0') {
  693. return TRUE;
  694. }
  695. PreStringTail = PreString + StringIndex;
  696. PreStringTailSize = PreStringSize - StringIndex;
  697. for (StringIndex = 0;
  698. StringIndex < PreStringTailSize;
  699. StringIndex += 1) {
  700. if ((DecodeEntry->PostParameterString[StringIndex] ==
  701. '\0') ||
  702. (DecodeEntry->PostParameterString[StringIndex] !=
  703. PreStringTail[StringIndex])) {
  704. break;
  705. }
  706. }
  707. if (StringIndex == PreStringTailSize) {
  708. return TRUE;
  709. }
  710. }
  711. return FALSE;
  712. }
  713. if (DecodeEntry->PreParameterString[StringIndex] != '\0') {
  714. *PartialMatch = TRUE;
  715. return FALSE;
  716. }
  717. //
  718. // If the post-parameter string is empty, return a partial match. The next
  719. // character (which should get glommed on to the pre-parameter string)
  720. // will make it complete.
  721. //
  722. if (DecodeEntry->PostParameterString[0] == '\0') {
  723. *PartialMatch = TRUE;
  724. return FALSE;
  725. }
  726. //
  727. // Match the post-parameter string.
  728. //
  729. for (StringIndex = 0; StringIndex < PostStringSize; StringIndex += 1) {
  730. if ((DecodeEntry->PostParameterString[StringIndex] == '\0') ||
  731. (DecodeEntry->PostParameterString[StringIndex] !=
  732. PostString[StringIndex])) {
  733. break;
  734. }
  735. }
  736. if (StringIndex != PostStringSize) {
  737. return FALSE;
  738. }
  739. if (DecodeEntry->PostParameterString[StringIndex] != '\0') {
  740. *PartialMatch = TRUE;
  741. return FALSE;
  742. }
  743. return TRUE;
  744. }