]> git.proxmox.com Git - mirror_edk2.git/blob - ShellPkg/Application/Shell/Shell.c
ShellPkg : Cache the environment variable into memory to enhance
[mirror_edk2.git] / ShellPkg / Application / Shell / Shell.c
1 /** @file
2 This is THE shell (application)
3
4 Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
5 (C) Copyright 2013-2014 Hewlett-Packard Development Company, L.P.<BR>
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
10
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13
14 **/
15
16 #include "Shell.h"
17
18 //
19 // Initialize the global structure
20 //
21 SHELL_INFO ShellInfoObject = {
22 NULL,
23 NULL,
24 FALSE,
25 FALSE,
26 {
27 {{
28 0,
29 0,
30 0,
31 0,
32 0,
33 0,
34 0,
35 0,
36 0,
37 0
38 }},
39 0,
40 NULL,
41 NULL
42 },
43 {{NULL, NULL}, NULL},
44 {
45 {{NULL, NULL}, NULL},
46 0,
47 0,
48 TRUE
49 },
50 NULL,
51 0,
52 NULL,
53 NULL,
54 NULL,
55 NULL,
56 NULL,
57 {{NULL, NULL}, NULL, NULL},
58 {{NULL, NULL}, NULL, NULL},
59 NULL,
60 NULL,
61 NULL,
62 NULL,
63 NULL,
64 NULL,
65 NULL,
66 NULL,
67 FALSE
68 };
69
70 STATIC CONST CHAR16 mScriptExtension[] = L".NSH";
71 STATIC CONST CHAR16 mExecutableExtensions[] = L".NSH;.EFI";
72 STATIC CONST CHAR16 mStartupScript[] = L"startup.nsh";
73 CONST CHAR16 mNoNestingEnvVarName[] = L"nonesting";
74 CONST CHAR16 mNoNestingTrue[] = L"True";
75 CONST CHAR16 mNoNestingFalse[] = L"False";
76
77 /**
78 Cleans off leading and trailing spaces and tabs.
79
80 @param[in] String pointer to the string to trim them off.
81 **/
82 EFI_STATUS
83 EFIAPI
84 TrimSpaces(
85 IN CHAR16 **String
86 )
87 {
88 ASSERT(String != NULL);
89 ASSERT(*String!= NULL);
90 //
91 // Remove any spaces and tabs at the beginning of the (*String).
92 //
93 while (((*String)[0] == L' ') || ((*String)[0] == L'\t')) {
94 CopyMem((*String), (*String)+1, StrSize((*String)) - sizeof((*String)[0]));
95 }
96
97 //
98 // Remove any spaces and tabs at the end of the (*String).
99 //
100 while ((StrLen (*String) > 0) && (((*String)[StrLen((*String))-1] == L' ') || ((*String)[StrLen((*String))-1] == L'\t'))) {
101 (*String)[StrLen((*String))-1] = CHAR_NULL;
102 }
103
104 return (EFI_SUCCESS);
105 }
106
107 /**
108 Parse for the next instance of one string within another string. Can optionally make sure that
109 the string was not escaped (^ character) per the shell specification.
110
111 @param[in] SourceString The string to search within
112 @param[in] FindString The string to look for
113 @param[in] CheckForEscapeCharacter TRUE to skip escaped instances of FinfString, otherwise will return even escaped instances
114 **/
115 CHAR16*
116 EFIAPI
117 FindNextInstance(
118 IN CONST CHAR16 *SourceString,
119 IN CONST CHAR16 *FindString,
120 IN CONST BOOLEAN CheckForEscapeCharacter
121 )
122 {
123 CHAR16 *Temp;
124 if (SourceString == NULL) {
125 return (NULL);
126 }
127 Temp = StrStr(SourceString, FindString);
128
129 //
130 // If nothing found, or we don't care about escape characters
131 //
132 if (Temp == NULL || !CheckForEscapeCharacter) {
133 return (Temp);
134 }
135
136 //
137 // If we found an escaped character, try again on the remainder of the string
138 //
139 if ((Temp > (SourceString)) && *(Temp-1) == L'^') {
140 return FindNextInstance(Temp+1, FindString, CheckForEscapeCharacter);
141 }
142
143 //
144 // we found the right character
145 //
146 return (Temp);
147 }
148
149 /**
150 Check whether the string between a pair of % is a valid environment variable name.
151
152 @param[in] BeginPercent pointer to the first percent.
153 @param[in] EndPercent pointer to the last percent.
154
155 @retval TRUE is a valid environment variable name.
156 @retval FALSE is NOT a valid environment variable name.
157 **/
158 BOOLEAN
159 IsValidEnvironmentVariableName(
160 IN CONST CHAR16 *BeginPercent,
161 IN CONST CHAR16 *EndPercent
162 )
163 {
164 CONST CHAR16 *Walker;
165
166 Walker = NULL;
167
168 ASSERT (BeginPercent != NULL);
169 ASSERT (EndPercent != NULL);
170 ASSERT (BeginPercent < EndPercent);
171
172 if ((BeginPercent + 1) == EndPercent) {
173 return FALSE;
174 }
175
176 for (Walker = BeginPercent + 1; Walker < EndPercent; Walker++) {
177 if (
178 (*Walker >= L'0' && *Walker <= L'9') ||
179 (*Walker >= L'A' && *Walker <= L'Z') ||
180 (*Walker >= L'a' && *Walker <= L'z') ||
181 (*Walker == L'_')
182 ) {
183 if (Walker == BeginPercent + 1 && (*Walker >= L'0' && *Walker <= L'9')) {
184 return FALSE;
185 } else {
186 continue;
187 }
188 } else {
189 return FALSE;
190 }
191 }
192
193 return TRUE;
194 }
195
196 /**
197 Determine if a command line contains a split operation
198
199 @param[in] CmdLine The command line to parse.
200
201 @retval TRUE CmdLine has a valid split.
202 @retval FALSE CmdLine does not have a valid split.
203 **/
204 BOOLEAN
205 EFIAPI
206 ContainsSplit(
207 IN CONST CHAR16 *CmdLine
208 )
209 {
210 CONST CHAR16 *TempSpot;
211 CONST CHAR16 *FirstQuote;
212 CONST CHAR16 *SecondQuote;
213
214 FirstQuote = FindNextInstance (CmdLine, L"\"", TRUE);
215 SecondQuote = NULL;
216 TempSpot = FindFirstCharacter(CmdLine, L"|", L'^');
217
218 if (FirstQuote == NULL ||
219 TempSpot == NULL ||
220 TempSpot == CHAR_NULL ||
221 FirstQuote > TempSpot
222 ) {
223 return (BOOLEAN) ((TempSpot != NULL) && (*TempSpot != CHAR_NULL));
224 }
225
226 while ((TempSpot != NULL) && (*TempSpot != CHAR_NULL)) {
227 if (FirstQuote == NULL || FirstQuote > TempSpot) {
228 break;
229 }
230 SecondQuote = FindNextInstance (FirstQuote + 1, L"\"", TRUE);
231 if (SecondQuote == NULL) {
232 break;
233 }
234 if (SecondQuote < TempSpot) {
235 FirstQuote = FindNextInstance (SecondQuote + 1, L"\"", TRUE);
236 continue;
237 } else {
238 FirstQuote = FindNextInstance (SecondQuote + 1, L"\"", TRUE);
239 TempSpot = FindFirstCharacter(TempSpot + 1, L"|", L'^');
240 continue;
241 }
242 }
243
244 return (BOOLEAN) ((TempSpot != NULL) && (*TempSpot != CHAR_NULL));
245 }
246
247 /**
248 Function to start monitoring for CTRL-S using SimpleTextInputEx. This
249 feature's enabled state was not known when the shell initially launched.
250
251 @retval EFI_SUCCESS The feature is enabled.
252 @retval EFI_OUT_OF_RESOURCES There is not enough memory available.
253 **/
254 EFI_STATUS
255 EFIAPI
256 InternalEfiShellStartCtrlSMonitor(
257 VOID
258 )
259 {
260 EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleEx;
261 EFI_KEY_DATA KeyData;
262 EFI_STATUS Status;
263
264 Status = gBS->OpenProtocol(
265 gST->ConsoleInHandle,
266 &gEfiSimpleTextInputExProtocolGuid,
267 (VOID**)&SimpleEx,
268 gImageHandle,
269 NULL,
270 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
271 if (EFI_ERROR(Status)) {
272 ShellPrintHiiEx(
273 -1,
274 -1,
275 NULL,
276 STRING_TOKEN (STR_SHELL_NO_IN_EX),
277 ShellInfoObject.HiiHandle);
278 return (EFI_SUCCESS);
279 }
280
281 KeyData.KeyState.KeyToggleState = 0;
282 KeyData.Key.ScanCode = 0;
283 KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;
284 KeyData.Key.UnicodeChar = L's';
285
286 Status = SimpleEx->RegisterKeyNotify(
287 SimpleEx,
288 &KeyData,
289 NotificationFunction,
290 &ShellInfoObject.CtrlSNotifyHandle1);
291
292 KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;
293 if (!EFI_ERROR(Status)) {
294 Status = SimpleEx->RegisterKeyNotify(
295 SimpleEx,
296 &KeyData,
297 NotificationFunction,
298 &ShellInfoObject.CtrlSNotifyHandle2);
299 }
300 KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;
301 KeyData.Key.UnicodeChar = 19;
302
303 if (!EFI_ERROR(Status)) {
304 Status = SimpleEx->RegisterKeyNotify(
305 SimpleEx,
306 &KeyData,
307 NotificationFunction,
308 &ShellInfoObject.CtrlSNotifyHandle3);
309 }
310 KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;
311 if (!EFI_ERROR(Status)) {
312 Status = SimpleEx->RegisterKeyNotify(
313 SimpleEx,
314 &KeyData,
315 NotificationFunction,
316 &ShellInfoObject.CtrlSNotifyHandle4);
317 }
318 return (Status);
319 }
320
321
322
323 /**
324 The entry point for the application.
325
326 @param[in] ImageHandle The firmware allocated handle for the EFI image.
327 @param[in] SystemTable A pointer to the EFI System Table.
328
329 @retval EFI_SUCCESS The entry point is executed successfully.
330 @retval other Some error occurs when executing this entry point.
331
332 **/
333 EFI_STATUS
334 EFIAPI
335 UefiMain (
336 IN EFI_HANDLE ImageHandle,
337 IN EFI_SYSTEM_TABLE *SystemTable
338 )
339 {
340 EFI_STATUS Status;
341 CHAR16 *TempString;
342 UINTN Size;
343 EFI_HANDLE ConInHandle;
344 EFI_SIMPLE_TEXT_INPUT_PROTOCOL *OldConIn;
345
346 if (PcdGet8(PcdShellSupportLevel) > 3) {
347 return (EFI_UNSUPPORTED);
348 }
349
350 //
351 // Clear the screen
352 //
353 Status = gST->ConOut->ClearScreen(gST->ConOut);
354 if (EFI_ERROR(Status)) {
355 return (Status);
356 }
357
358 //
359 // Populate the global structure from PCDs
360 //
361 ShellInfoObject.ImageDevPath = NULL;
362 ShellInfoObject.FileDevPath = NULL;
363 ShellInfoObject.PageBreakEnabled = PcdGetBool(PcdShellPageBreakDefault);
364 ShellInfoObject.ViewingSettings.InsertMode = PcdGetBool(PcdShellInsertModeDefault);
365 ShellInfoObject.LogScreenCount = PcdGet8 (PcdShellScreenLogCount );
366
367 //
368 // verify we dont allow for spec violation
369 //
370 ASSERT(ShellInfoObject.LogScreenCount >= 3);
371
372 //
373 // Initialize the LIST ENTRY objects...
374 //
375 InitializeListHead(&ShellInfoObject.BufferToFreeList.Link);
376 InitializeListHead(&ShellInfoObject.ViewingSettings.CommandHistory.Link);
377 InitializeListHead(&ShellInfoObject.SplitList.Link);
378
379 //
380 // Check PCDs for optional features that are not implemented yet.
381 //
382 if ( PcdGetBool(PcdShellSupportOldProtocols)
383 || !FeaturePcdGet(PcdShellRequireHiiPlatform)
384 || FeaturePcdGet(PcdShellSupportFrameworkHii)
385 ) {
386 return (EFI_UNSUPPORTED);
387 }
388
389 //
390 // turn off the watchdog timer
391 //
392 gBS->SetWatchdogTimer (0, 0, 0, NULL);
393
394 //
395 // install our console logger. This will keep a log of the output for back-browsing
396 //
397 Status = ConsoleLoggerInstall(ShellInfoObject.LogScreenCount, &ShellInfoObject.ConsoleInfo);
398 if (!EFI_ERROR(Status)) {
399 //
400 // Enable the cursor to be visible
401 //
402 gST->ConOut->EnableCursor (gST->ConOut, TRUE);
403
404 //
405 // If supporting EFI 1.1 we need to install HII protocol
406 // only do this if PcdShellRequireHiiPlatform == FALSE
407 //
408 // remove EFI_UNSUPPORTED check above when complete.
409 ///@todo add support for Framework HII
410
411 //
412 // install our (solitary) HII package
413 //
414 ShellInfoObject.HiiHandle = HiiAddPackages (&gEfiCallerIdGuid, gImageHandle, ShellStrings, NULL);
415 if (ShellInfoObject.HiiHandle == NULL) {
416 if (PcdGetBool(PcdShellSupportFrameworkHii)) {
417 ///@todo Add our package into Framework HII
418 }
419 if (ShellInfoObject.HiiHandle == NULL) {
420 Status = EFI_NOT_STARTED;
421 goto FreeResources;
422 }
423 }
424
425 //
426 // create and install the EfiShellParametersProtocol
427 //
428 Status = CreatePopulateInstallShellParametersProtocol(&ShellInfoObject.NewShellParametersProtocol, &ShellInfoObject.RootShellInstance);
429 ASSERT_EFI_ERROR(Status);
430 ASSERT(ShellInfoObject.NewShellParametersProtocol != NULL);
431
432 //
433 // create and install the EfiShellProtocol
434 //
435 Status = CreatePopulateInstallShellProtocol(&ShellInfoObject.NewEfiShellProtocol);
436 ASSERT_EFI_ERROR(Status);
437 ASSERT(ShellInfoObject.NewEfiShellProtocol != NULL);
438
439 //
440 // Now initialize the shell library (it requires Shell Parameters protocol)
441 //
442 Status = ShellInitialize();
443 ASSERT_EFI_ERROR(Status);
444
445 Status = CommandInit();
446 ASSERT_EFI_ERROR(Status);
447
448 Status = ShellInitEnvVarList ();
449
450 //
451 // Check the command line
452 //
453 Status = ProcessCommandLine ();
454 if (EFI_ERROR (Status)) {
455 goto FreeResources;
456 }
457
458 //
459 // If shell support level is >= 1 create the mappings and paths
460 //
461 if (PcdGet8(PcdShellSupportLevel) >= 1) {
462 Status = ShellCommandCreateInitialMappingsAndPaths();
463 }
464
465 //
466 // Set the environment variable for nesting support
467 //
468 Size = 0;
469 TempString = NULL;
470 if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoNest) {
471 //
472 // No change. require nesting in Shell Protocol Execute()
473 //
474 StrnCatGrow(&TempString,
475 &Size,
476 L"False",
477 0);
478 } else {
479 StrnCatGrow(&TempString,
480 &Size,
481 mNoNestingTrue,
482 0);
483 }
484 Status = InternalEfiShellSetEnv(mNoNestingEnvVarName, TempString, TRUE);
485 SHELL_FREE_NON_NULL(TempString);
486 Size = 0;
487
488 //
489 // save the device path for the loaded image and the device path for the filepath (under loaded image)
490 // These are where to look for the startup.nsh file
491 //
492 Status = GetDevicePathsForImageAndFile(&ShellInfoObject.ImageDevPath, &ShellInfoObject.FileDevPath);
493 ASSERT_EFI_ERROR(Status);
494
495 //
496 // Display the version
497 //
498 if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion) {
499 ShellPrintHiiEx (
500 0,
501 gST->ConOut->Mode->CursorRow,
502 NULL,
503 STRING_TOKEN (STR_VER_OUTPUT_MAIN_SHELL),
504 ShellInfoObject.HiiHandle,
505 SupportLevel[PcdGet8(PcdShellSupportLevel)],
506 gEfiShellProtocol->MajorVersion,
507 gEfiShellProtocol->MinorVersion
508 );
509
510 ShellPrintHiiEx (
511 -1,
512 -1,
513 NULL,
514 STRING_TOKEN (STR_VER_OUTPUT_MAIN_SUPPLIER),
515 ShellInfoObject.HiiHandle,
516 (CHAR16 *) PcdGetPtr (PcdShellSupplier)
517 );
518
519 ShellPrintHiiEx (
520 -1,
521 -1,
522 NULL,
523 STRING_TOKEN (STR_VER_OUTPUT_MAIN_UEFI),
524 ShellInfoObject.HiiHandle,
525 (gST->Hdr.Revision&0xffff0000)>>16,
526 (gST->Hdr.Revision&0x0000ffff),
527 gST->FirmwareVendor,
528 gST->FirmwareRevision
529 );
530 }
531
532 //
533 // Display the mapping
534 //
535 if (PcdGet8(PcdShellSupportLevel) >= 2 && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap) {
536 Status = RunCommand(L"map");
537 ASSERT_EFI_ERROR(Status);
538 }
539
540 //
541 // init all the built in alias'
542 //
543 Status = SetBuiltInAlias();
544 ASSERT_EFI_ERROR(Status);
545
546 //
547 // Initialize environment variables
548 //
549 if (ShellCommandGetProfileList() != NULL) {
550 Status = InternalEfiShellSetEnv(L"profiles", ShellCommandGetProfileList(), TRUE);
551 ASSERT_EFI_ERROR(Status);
552 }
553
554 Size = 100;
555 TempString = AllocateZeroPool(Size);
556
557 UnicodeSPrint(TempString, Size, L"%d", PcdGet8(PcdShellSupportLevel));
558 Status = InternalEfiShellSetEnv(L"uefishellsupport", TempString, TRUE);
559 ASSERT_EFI_ERROR(Status);
560
561 UnicodeSPrint(TempString, Size, L"%d.%d", ShellInfoObject.NewEfiShellProtocol->MajorVersion, ShellInfoObject.NewEfiShellProtocol->MinorVersion);
562 Status = InternalEfiShellSetEnv(L"uefishellversion", TempString, TRUE);
563 ASSERT_EFI_ERROR(Status);
564
565 UnicodeSPrint(TempString, Size, L"%d.%d", (gST->Hdr.Revision & 0xFFFF0000) >> 16, gST->Hdr.Revision & 0x0000FFFF);
566 Status = InternalEfiShellSetEnv(L"uefiversion", TempString, TRUE);
567 ASSERT_EFI_ERROR(Status);
568
569 FreePool(TempString);
570
571 if (!EFI_ERROR(Status)) {
572 if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt) {
573 //
574 // Set up the event for CTRL-C monitoring...
575 //
576 Status = InernalEfiShellStartMonitor();
577 }
578
579 if (!EFI_ERROR(Status) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {
580 //
581 // Set up the event for CTRL-S monitoring...
582 //
583 Status = InternalEfiShellStartCtrlSMonitor();
584 }
585
586 if (!EFI_ERROR(Status) && ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {
587 //
588 // close off the gST->ConIn
589 //
590 OldConIn = gST->ConIn;
591 ConInHandle = gST->ConsoleInHandle;
592 gST->ConIn = CreateSimpleTextInOnFile((SHELL_FILE_HANDLE)&FileInterfaceNulFile, &gST->ConsoleInHandle);
593 } else {
594 OldConIn = NULL;
595 ConInHandle = NULL;
596 }
597
598 if (!EFI_ERROR(Status) && PcdGet8(PcdShellSupportLevel) >= 1) {
599 //
600 // process the startup script or launch the called app.
601 //
602 Status = DoStartupScript(ShellInfoObject.ImageDevPath, ShellInfoObject.FileDevPath);
603 }
604
605 if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit && !ShellCommandGetExit() && (PcdGet8(PcdShellSupportLevel) >= 3 || PcdGetBool(PcdShellForceConsole)) && !EFI_ERROR(Status) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {
606 //
607 // begin the UI waiting loop
608 //
609 do {
610 //
611 // clean out all the memory allocated for CONST <something> * return values
612 // between each shell prompt presentation
613 //
614 if (!IsListEmpty(&ShellInfoObject.BufferToFreeList.Link)){
615 FreeBufferList(&ShellInfoObject.BufferToFreeList);
616 }
617
618 //
619 // Reset page break back to default.
620 //
621 ShellInfoObject.PageBreakEnabled = PcdGetBool(PcdShellPageBreakDefault);
622 ASSERT (ShellInfoObject.ConsoleInfo != NULL);
623 ShellInfoObject.ConsoleInfo->Enabled = TRUE;
624 ShellInfoObject.ConsoleInfo->RowCounter = 0;
625
626 //
627 // Reset the CTRL-C event (yes we ignore the return values)
628 //
629 Status = gBS->CheckEvent (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak);
630
631 //
632 // Display Prompt
633 //
634 Status = DoShellPrompt();
635 } while (!ShellCommandGetExit());
636 }
637 if (OldConIn != NULL && ConInHandle != NULL) {
638 CloseSimpleTextInOnFile (gST->ConIn);
639 gST->ConIn = OldConIn;
640 gST->ConsoleInHandle = ConInHandle;
641 }
642 }
643 }
644
645 FreeResources:
646 //
647 // uninstall protocols / free memory / etc...
648 //
649 if (ShellInfoObject.UserBreakTimer != NULL) {
650 gBS->CloseEvent(ShellInfoObject.UserBreakTimer);
651 DEBUG_CODE(ShellInfoObject.UserBreakTimer = NULL;);
652 }
653 if (ShellInfoObject.ImageDevPath != NULL) {
654 FreePool(ShellInfoObject.ImageDevPath);
655 DEBUG_CODE(ShellInfoObject.ImageDevPath = NULL;);
656 }
657 if (ShellInfoObject.FileDevPath != NULL) {
658 FreePool(ShellInfoObject.FileDevPath);
659 DEBUG_CODE(ShellInfoObject.FileDevPath = NULL;);
660 }
661 if (ShellInfoObject.NewShellParametersProtocol != NULL) {
662 CleanUpShellParametersProtocol(ShellInfoObject.NewShellParametersProtocol);
663 DEBUG_CODE(ShellInfoObject.NewShellParametersProtocol = NULL;);
664 }
665 if (ShellInfoObject.NewEfiShellProtocol != NULL){
666 if (ShellInfoObject.NewEfiShellProtocol->IsRootShell()){
667 InternalEfiShellSetEnv(L"cwd", NULL, TRUE);
668 }
669 CleanUpShellProtocol(ShellInfoObject.NewEfiShellProtocol);
670 DEBUG_CODE(ShellInfoObject.NewEfiShellProtocol = NULL;);
671 }
672
673 if (!IsListEmpty(&ShellInfoObject.BufferToFreeList.Link)){
674 FreeBufferList(&ShellInfoObject.BufferToFreeList);
675 }
676
677 if (!IsListEmpty(&ShellInfoObject.SplitList.Link)){
678 ASSERT(FALSE); ///@todo finish this de-allocation.
679 }
680
681 if (ShellInfoObject.ShellInitSettings.FileName != NULL) {
682 FreePool(ShellInfoObject.ShellInitSettings.FileName);
683 DEBUG_CODE(ShellInfoObject.ShellInitSettings.FileName = NULL;);
684 }
685
686 if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {
687 FreePool(ShellInfoObject.ShellInitSettings.FileOptions);
688 DEBUG_CODE(ShellInfoObject.ShellInitSettings.FileOptions = NULL;);
689 }
690
691 if (ShellInfoObject.HiiHandle != NULL) {
692 HiiRemovePackages(ShellInfoObject.HiiHandle);
693 DEBUG_CODE(ShellInfoObject.HiiHandle = NULL;);
694 }
695
696 if (!IsListEmpty(&ShellInfoObject.ViewingSettings.CommandHistory.Link)){
697 FreeBufferList(&ShellInfoObject.ViewingSettings.CommandHistory);
698 }
699
700 ASSERT(ShellInfoObject.ConsoleInfo != NULL);
701 if (ShellInfoObject.ConsoleInfo != NULL) {
702 ConsoleLoggerUninstall(ShellInfoObject.ConsoleInfo);
703 FreePool(ShellInfoObject.ConsoleInfo);
704 DEBUG_CODE(ShellInfoObject.ConsoleInfo = NULL;);
705 }
706
707 ShellFreeEnvVarList ();
708
709 if (ShellCommandGetExit()) {
710 return ((EFI_STATUS)ShellCommandGetExitCode());
711 }
712 return (Status);
713 }
714
715 /**
716 Sets all the alias' that were registered with the ShellCommandLib library.
717
718 @retval EFI_SUCCESS all init commands were run successfully.
719 **/
720 EFI_STATUS
721 EFIAPI
722 SetBuiltInAlias(
723 )
724 {
725 EFI_STATUS Status;
726 CONST ALIAS_LIST *List;
727 ALIAS_LIST *Node;
728
729 //
730 // Get all the commands we want to run
731 //
732 List = ShellCommandGetInitAliasList();
733
734 //
735 // for each command in the List
736 //
737 for ( Node = (ALIAS_LIST*)GetFirstNode(&List->Link)
738 ; !IsNull (&List->Link, &Node->Link)
739 ; Node = (ALIAS_LIST *)GetNextNode(&List->Link, &Node->Link)
740 ){
741 //
742 // install the alias'
743 //
744 Status = InternalSetAlias(Node->CommandString, Node->Alias, TRUE);
745 ASSERT_EFI_ERROR(Status);
746 }
747 return (EFI_SUCCESS);
748 }
749
750 /**
751 Internal function to determine if 2 command names are really the same.
752
753 @param[in] Command1 The pointer to the first command name.
754 @param[in] Command2 The pointer to the second command name.
755
756 @retval TRUE The 2 command names are the same.
757 @retval FALSE The 2 command names are not the same.
758 **/
759 BOOLEAN
760 EFIAPI
761 IsCommand(
762 IN CONST CHAR16 *Command1,
763 IN CONST CHAR16 *Command2
764 )
765 {
766 if (StringNoCaseCompare(&Command1, &Command2) == 0) {
767 return (TRUE);
768 }
769 return (FALSE);
770 }
771
772 /**
773 Internal function to determine if a command is a script only command.
774
775 @param[in] CommandName The pointer to the command name.
776
777 @retval TRUE The command is a script only command.
778 @retval FALSE The command is not a script only command.
779 **/
780 BOOLEAN
781 EFIAPI
782 IsScriptOnlyCommand(
783 IN CONST CHAR16 *CommandName
784 )
785 {
786 if (IsCommand(CommandName, L"for")
787 ||IsCommand(CommandName, L"endfor")
788 ||IsCommand(CommandName, L"if")
789 ||IsCommand(CommandName, L"else")
790 ||IsCommand(CommandName, L"endif")
791 ||IsCommand(CommandName, L"goto")) {
792 return (TRUE);
793 }
794 return (FALSE);
795 }
796
797 /**
798 This function will populate the 2 device path protocol parameters based on the
799 global gImageHandle. The DevPath will point to the device path for the handle that has
800 loaded image protocol installed on it. The FilePath will point to the device path
801 for the file that was loaded.
802
803 @param[in, out] DevPath On a successful return the device path to the loaded image.
804 @param[in, out] FilePath On a successful return the device path to the file.
805
806 @retval EFI_SUCCESS The 2 device paths were successfully returned.
807 @retval other A error from gBS->HandleProtocol.
808
809 @sa HandleProtocol
810 **/
811 EFI_STATUS
812 EFIAPI
813 GetDevicePathsForImageAndFile (
814 IN OUT EFI_DEVICE_PATH_PROTOCOL **DevPath,
815 IN OUT EFI_DEVICE_PATH_PROTOCOL **FilePath
816 )
817 {
818 EFI_STATUS Status;
819 EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
820 EFI_DEVICE_PATH_PROTOCOL *ImageDevicePath;
821
822 ASSERT(DevPath != NULL);
823 ASSERT(FilePath != NULL);
824
825 Status = gBS->OpenProtocol (
826 gImageHandle,
827 &gEfiLoadedImageProtocolGuid,
828 (VOID**)&LoadedImage,
829 gImageHandle,
830 NULL,
831 EFI_OPEN_PROTOCOL_GET_PROTOCOL
832 );
833 if (!EFI_ERROR (Status)) {
834 Status = gBS->OpenProtocol (
835 LoadedImage->DeviceHandle,
836 &gEfiDevicePathProtocolGuid,
837 (VOID**)&ImageDevicePath,
838 gImageHandle,
839 NULL,
840 EFI_OPEN_PROTOCOL_GET_PROTOCOL
841 );
842 if (!EFI_ERROR (Status)) {
843 *DevPath = DuplicateDevicePath (ImageDevicePath);
844 *FilePath = DuplicateDevicePath (LoadedImage->FilePath);
845 gBS->CloseProtocol(
846 LoadedImage->DeviceHandle,
847 &gEfiDevicePathProtocolGuid,
848 gImageHandle,
849 NULL);
850 }
851 gBS->CloseProtocol(
852 gImageHandle,
853 &gEfiLoadedImageProtocolGuid,
854 gImageHandle,
855 NULL);
856 }
857 return (Status);
858 }
859
860 /**
861 Process all Uefi Shell 2.0 command line options.
862
863 see Uefi Shell 2.0 section 3.2 for full details.
864
865 the command line must resemble the following:
866
867 shell.efi [ShellOpt-options] [options] [file-name [file-name-options]]
868
869 ShellOpt-options Options which control the initialization behavior of the shell.
870 These options are read from the EFI global variable "ShellOpt"
871 and are processed before options or file-name.
872
873 options Options which control the initialization behavior of the shell.
874
875 file-name The name of a UEFI shell application or script to be executed
876 after initialization is complete. By default, if file-name is
877 specified, then -nostartup is implied. Scripts are not supported
878 by level 0.
879
880 file-name-options The command-line options that are passed to file-name when it
881 is invoked.
882
883 This will initialize the ShellInfoObject.ShellInitSettings global variable.
884
885 @retval EFI_SUCCESS The variable is initialized.
886 **/
887 EFI_STATUS
888 EFIAPI
889 ProcessCommandLine(
890 VOID
891 )
892 {
893 UINTN Size;
894 UINTN LoopVar;
895 CHAR16 *CurrentArg;
896 CHAR16 *DelayValueStr;
897 UINT64 DelayValue;
898 EFI_STATUS Status;
899 EFI_UNICODE_COLLATION_PROTOCOL *UnicodeCollation;
900
901 // `file-name-options` will contain arguments to `file-name` that we don't
902 // know about. This would cause ShellCommandLineParse to error, so we parse
903 // arguments manually, ignoring those after the first thing that doesn't look
904 // like a shell option (which is assumed to be `file-name`).
905
906 Status = gBS->LocateProtocol (
907 &gEfiUnicodeCollationProtocolGuid,
908 NULL,
909 (VOID **) &UnicodeCollation
910 );
911 if (EFI_ERROR (Status)) {
912 return Status;
913 }
914
915 // Set default options
916 ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup = FALSE;
917 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = FALSE;
918 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut = FALSE;
919 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn = FALSE;
920 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt = FALSE;
921 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap = FALSE;
922 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion = FALSE;
923 ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay = FALSE;
924 ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit = FALSE;
925 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoNest = FALSE;
926 ShellInfoObject.ShellInitSettings.Delay = 5;
927
928 //
929 // Start LoopVar at 0 to parse only optional arguments at Argv[0]
930 // and parse other parameters from Argv[1]. This is for use case that
931 // UEFI Shell boot option is created, and OptionalData is provided
932 // that starts with shell command-line options.
933 //
934 for (LoopVar = 0 ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {
935 CurrentArg = gEfiShellParametersProtocol->Argv[LoopVar];
936 if (UnicodeCollation->StriColl (
937 UnicodeCollation,
938 L"-startup",
939 CurrentArg
940 ) == 0) {
941 ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup = TRUE;
942 }
943 else if (UnicodeCollation->StriColl (
944 UnicodeCollation,
945 L"-nostartup",
946 CurrentArg
947 ) == 0) {
948 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = TRUE;
949 }
950 else if (UnicodeCollation->StriColl (
951 UnicodeCollation,
952 L"-noconsoleout",
953 CurrentArg
954 ) == 0) {
955 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut = TRUE;
956 }
957 else if (UnicodeCollation->StriColl (
958 UnicodeCollation,
959 L"-noconsolein",
960 CurrentArg
961 ) == 0) {
962 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn = TRUE;
963 }
964 else if (UnicodeCollation->StriColl (
965 UnicodeCollation,
966 L"-nointerrupt",
967 CurrentArg
968 ) == 0) {
969 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt = TRUE;
970 }
971 else if (UnicodeCollation->StriColl (
972 UnicodeCollation,
973 L"-nomap",
974 CurrentArg
975 ) == 0) {
976 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap = TRUE;
977 }
978 else if (UnicodeCollation->StriColl (
979 UnicodeCollation,
980 L"-noversion",
981 CurrentArg
982 ) == 0) {
983 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion = TRUE;
984 }
985 else if (UnicodeCollation->StriColl (
986 UnicodeCollation,
987 L"-nonest",
988 CurrentArg
989 ) == 0) {
990 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoNest = TRUE;
991 }
992 else if (UnicodeCollation->StriColl (
993 UnicodeCollation,
994 L"-delay",
995 CurrentArg
996 ) == 0) {
997 ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay = TRUE;
998 // Check for optional delay value following "-delay"
999 DelayValueStr = gEfiShellParametersProtocol->Argv[LoopVar + 1];
1000 if (DelayValueStr != NULL){
1001 if (*DelayValueStr == L':') {
1002 DelayValueStr++;
1003 }
1004 if (!EFI_ERROR(ShellConvertStringToUint64 (
1005 DelayValueStr,
1006 &DelayValue,
1007 FALSE,
1008 FALSE
1009 ))) {
1010 ShellInfoObject.ShellInitSettings.Delay = (UINTN)DelayValue;
1011 LoopVar++;
1012 }
1013 }
1014 } else if (UnicodeCollation->StriColl (
1015 UnicodeCollation,
1016 L"-_exit",
1017 CurrentArg
1018 ) == 0) {
1019 ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit = TRUE;
1020 } else if (StrnCmp (L"-", CurrentArg, 1) == 0) {
1021 // Unrecognized option
1022 ShellPrintHiiEx(-1, -1, NULL,
1023 STRING_TOKEN (STR_GEN_PROBLEM),
1024 ShellInfoObject.HiiHandle,
1025 CurrentArg
1026 );
1027 return EFI_INVALID_PARAMETER;
1028 } else {
1029 //
1030 // First argument should be Shell.efi image name
1031 //
1032 if (LoopVar == 0) {
1033 continue;
1034 }
1035
1036 ShellInfoObject.ShellInitSettings.FileName = AllocateCopyPool(StrSize(CurrentArg), CurrentArg);
1037 if (ShellInfoObject.ShellInitSettings.FileName == NULL) {
1038 return (EFI_OUT_OF_RESOURCES);
1039 }
1040 //
1041 // We found `file-name`.
1042 //
1043 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = 1;
1044 LoopVar++;
1045
1046 // Add `file-name-options`
1047 for (Size = 0 ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {
1048 ASSERT((ShellInfoObject.ShellInitSettings.FileOptions == NULL && Size == 0) || (ShellInfoObject.ShellInitSettings.FileOptions != NULL));
1049 StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions,
1050 &Size,
1051 L" ",
1052 0);
1053 if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {
1054 SHELL_FREE_NON_NULL(ShellInfoObject.ShellInitSettings.FileName);
1055 return (EFI_OUT_OF_RESOURCES);
1056 }
1057 StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions,
1058 &Size,
1059 gEfiShellParametersProtocol->Argv[LoopVar],
1060 0);
1061 if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {
1062 SHELL_FREE_NON_NULL(ShellInfoObject.ShellInitSettings.FileName);
1063 return (EFI_OUT_OF_RESOURCES);
1064 }
1065 }
1066 }
1067 }
1068
1069 // "-nointerrupt" overrides "-delay"
1070 if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt) {
1071 ShellInfoObject.ShellInitSettings.Delay = 0;
1072 }
1073
1074 return EFI_SUCCESS;
1075 }
1076
1077 /**
1078 Handles all interaction with the default startup script.
1079
1080 this will check that the correct command line parameters were passed, handle the delay, and then start running the script.
1081
1082 @param ImagePath the path to the image for shell. first place to look for the startup script
1083 @param FilePath the path to the file for shell. second place to look for the startup script.
1084
1085 @retval EFI_SUCCESS the variable is initialized.
1086 **/
1087 EFI_STATUS
1088 EFIAPI
1089 DoStartupScript(
1090 IN EFI_DEVICE_PATH_PROTOCOL *ImagePath,
1091 IN EFI_DEVICE_PATH_PROTOCOL *FilePath
1092 )
1093 {
1094 EFI_STATUS Status;
1095 EFI_STATUS CalleeStatus;
1096 UINTN Delay;
1097 EFI_INPUT_KEY Key;
1098 SHELL_FILE_HANDLE FileHandle;
1099 EFI_DEVICE_PATH_PROTOCOL *NewPath;
1100 EFI_DEVICE_PATH_PROTOCOL *NamePath;
1101 CHAR16 *FileStringPath;
1102 CHAR16 *TempSpot;
1103 UINTN NewSize;
1104 CONST CHAR16 *MapName;
1105
1106 Key.UnicodeChar = CHAR_NULL;
1107 Key.ScanCode = 0;
1108 FileHandle = NULL;
1109
1110 if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup && ShellInfoObject.ShellInitSettings.FileName != NULL) {
1111 //
1112 // launch something else instead
1113 //
1114 NewSize = StrSize(ShellInfoObject.ShellInitSettings.FileName);
1115 if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {
1116 NewSize += StrSize(ShellInfoObject.ShellInitSettings.FileOptions) + sizeof(CHAR16);
1117 }
1118 FileStringPath = AllocateZeroPool(NewSize);
1119 if (FileStringPath == NULL) {
1120 return (EFI_OUT_OF_RESOURCES);
1121 }
1122 StrCpyS(FileStringPath, NewSize/sizeof(CHAR16), ShellInfoObject.ShellInitSettings.FileName);
1123 if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {
1124 StrnCatS(FileStringPath, NewSize/sizeof(CHAR16), L" ", NewSize/sizeof(CHAR16) - StrLen(FileStringPath) -1);
1125 StrnCatS(FileStringPath, NewSize/sizeof(CHAR16), ShellInfoObject.ShellInitSettings.FileOptions, NewSize/sizeof(CHAR16) - StrLen(FileStringPath) -1);
1126 }
1127 Status = RunShellCommand(FileStringPath, &CalleeStatus);
1128 if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit == TRUE) {
1129 ShellCommandRegisterExit(gEfiShellProtocol->BatchIsActive(), (UINT64)CalleeStatus);
1130 }
1131 FreePool(FileStringPath);
1132 return (Status);
1133
1134 }
1135
1136 //
1137 // for shell level 0 we do no scripts
1138 // Without the Startup bit overriding we allow for nostartup to prevent scripts
1139 //
1140 if ( (PcdGet8(PcdShellSupportLevel) < 1)
1141 || (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup)
1142 ){
1143 return (EFI_SUCCESS);
1144 }
1145
1146 gST->ConOut->EnableCursor(gST->ConOut, FALSE);
1147 //
1148 // print out our warning and see if they press a key
1149 //
1150 for ( Status = EFI_UNSUPPORTED, Delay = ShellInfoObject.ShellInitSettings.Delay
1151 ; Delay != 0 && EFI_ERROR(Status)
1152 ; Delay--
1153 ){
1154 ShellPrintHiiEx(0, gST->ConOut->Mode->CursorRow, NULL, STRING_TOKEN (STR_SHELL_STARTUP_QUESTION), ShellInfoObject.HiiHandle, Delay);
1155 gBS->Stall (1000000);
1156 if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {
1157 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
1158 }
1159 }
1160 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CRLF), ShellInfoObject.HiiHandle);
1161 gST->ConOut->EnableCursor(gST->ConOut, TRUE);
1162
1163 //
1164 // ESC was pressed
1165 //
1166 if (Status == EFI_SUCCESS && Key.UnicodeChar == 0 && Key.ScanCode == SCAN_ESC) {
1167 return (EFI_SUCCESS);
1168 }
1169
1170 //
1171 // Try the first location (must be file system)
1172 //
1173 MapName = ShellInfoObject.NewEfiShellProtocol->GetMapFromDevicePath(&ImagePath);
1174 if (MapName != NULL) {
1175 FileStringPath = NULL;
1176 NewSize = 0;
1177 FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, MapName, 0);
1178 if (FileStringPath == NULL) {
1179 Status = EFI_OUT_OF_RESOURCES;
1180 } else {
1181 TempSpot = StrStr(FileStringPath, L";");
1182 if (TempSpot != NULL) {
1183 *TempSpot = CHAR_NULL;
1184 }
1185 FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, ((FILEPATH_DEVICE_PATH*)FilePath)->PathName, 0);
1186 PathRemoveLastItem(FileStringPath);
1187 FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, mStartupScript, 0);
1188 Status = ShellInfoObject.NewEfiShellProtocol->OpenFileByName(FileStringPath, &FileHandle, EFI_FILE_MODE_READ);
1189 FreePool(FileStringPath);
1190 }
1191 }
1192 if (EFI_ERROR(Status)) {
1193 NamePath = FileDevicePath (NULL, mStartupScript);
1194 NewPath = AppendDevicePathNode (ImagePath, NamePath);
1195 FreePool(NamePath);
1196
1197 //
1198 // Try the location
1199 //
1200 Status = InternalOpenFileDevicePath(NewPath, &FileHandle, EFI_FILE_MODE_READ, 0);
1201 FreePool(NewPath);
1202 }
1203 //
1204 // If we got a file, run it
1205 //
1206 if (!EFI_ERROR(Status) && FileHandle != NULL) {
1207 Status = RunScriptFile (mStartupScript, FileHandle, L"", ShellInfoObject.NewShellParametersProtocol);
1208 ShellInfoObject.NewEfiShellProtocol->CloseFile(FileHandle);
1209 } else {
1210 FileStringPath = ShellFindFilePath(mStartupScript);
1211 if (FileStringPath == NULL) {
1212 //
1213 // we return success since we don't need to have a startup script
1214 //
1215 Status = EFI_SUCCESS;
1216 ASSERT(FileHandle == NULL);
1217 } else {
1218 Status = RunScriptFile(FileStringPath, NULL, L"", ShellInfoObject.NewShellParametersProtocol);
1219 FreePool(FileStringPath);
1220 }
1221 }
1222
1223
1224 return (Status);
1225 }
1226
1227 /**
1228 Function to perform the shell prompt looping. It will do a single prompt,
1229 dispatch the result, and then return. It is expected that the caller will
1230 call this function in a loop many times.
1231
1232 @retval EFI_SUCCESS
1233 @retval RETURN_ABORTED
1234 **/
1235 EFI_STATUS
1236 EFIAPI
1237 DoShellPrompt (
1238 VOID
1239 )
1240 {
1241 UINTN Column;
1242 UINTN Row;
1243 CHAR16 *CmdLine;
1244 CONST CHAR16 *CurDir;
1245 UINTN BufferSize;
1246 EFI_STATUS Status;
1247 LIST_ENTRY OldBufferList;
1248
1249 CurDir = NULL;
1250
1251 //
1252 // Get screen setting to decide size of the command line buffer
1253 //
1254 gST->ConOut->QueryMode (gST->ConOut, gST->ConOut->Mode->Mode, &Column, &Row);
1255 BufferSize = Column * Row * sizeof (CHAR16);
1256 CmdLine = AllocateZeroPool (BufferSize);
1257 if (CmdLine == NULL) {
1258 return EFI_OUT_OF_RESOURCES;
1259 }
1260
1261 SaveBufferList(&OldBufferList);
1262 CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv(L"cwd");
1263
1264 //
1265 // Prompt for input
1266 //
1267 gST->ConOut->SetCursorPosition (gST->ConOut, 0, gST->ConOut->Mode->CursorRow);
1268
1269 if (CurDir != NULL && StrLen(CurDir) > 1) {
1270 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CURDIR), ShellInfoObject.HiiHandle, CurDir);
1271 } else {
1272 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_SHELL), ShellInfoObject.HiiHandle);
1273 }
1274
1275 //
1276 // Read a line from the console
1277 //
1278 Status = ShellInfoObject.NewEfiShellProtocol->ReadFile(ShellInfoObject.NewShellParametersProtocol->StdIn, &BufferSize, CmdLine);
1279
1280 //
1281 // Null terminate the string and parse it
1282 //
1283 if (!EFI_ERROR (Status)) {
1284 CmdLine[BufferSize / sizeof (CHAR16)] = CHAR_NULL;
1285 Status = RunCommand(CmdLine);
1286 }
1287
1288 //
1289 // Done with this command
1290 //
1291 RestoreBufferList(&OldBufferList);
1292 FreePool (CmdLine);
1293 return Status;
1294 }
1295
1296 /**
1297 Add a buffer to the Buffer To Free List for safely returning buffers to other
1298 places without risking letting them modify internal shell information.
1299
1300 @param Buffer Something to pass to FreePool when the shell is exiting.
1301 **/
1302 VOID*
1303 EFIAPI
1304 AddBufferToFreeList(
1305 VOID *Buffer
1306 )
1307 {
1308 BUFFER_LIST *BufferListEntry;
1309
1310 if (Buffer == NULL) {
1311 return (NULL);
1312 }
1313
1314 BufferListEntry = AllocateZeroPool(sizeof(BUFFER_LIST));
1315 ASSERT(BufferListEntry != NULL);
1316 BufferListEntry->Buffer = Buffer;
1317 InsertTailList(&ShellInfoObject.BufferToFreeList.Link, &BufferListEntry->Link);
1318 return (Buffer);
1319 }
1320
1321
1322 /**
1323 Create a new buffer list and stores the old one to OldBufferList
1324
1325 @param OldBufferList The temporary list head used to store the nodes in BufferToFreeList.
1326 **/
1327 VOID
1328 SaveBufferList (
1329 OUT LIST_ENTRY *OldBufferList
1330 )
1331 {
1332 CopyMem (OldBufferList, &ShellInfoObject.BufferToFreeList.Link, sizeof (LIST_ENTRY));
1333 InitializeListHead (&ShellInfoObject.BufferToFreeList.Link);
1334 }
1335
1336 /**
1337 Restore previous nodes into BufferToFreeList .
1338
1339 @param OldBufferList The temporary list head used to store the nodes in BufferToFreeList.
1340 **/
1341 VOID
1342 RestoreBufferList (
1343 IN OUT LIST_ENTRY *OldBufferList
1344 )
1345 {
1346 FreeBufferList (&ShellInfoObject.BufferToFreeList);
1347 CopyMem (&ShellInfoObject.BufferToFreeList.Link, OldBufferList, sizeof (LIST_ENTRY));
1348 }
1349
1350
1351 /**
1352 Add a buffer to the Line History List
1353
1354 @param Buffer The line buffer to add.
1355 **/
1356 VOID
1357 EFIAPI
1358 AddLineToCommandHistory(
1359 IN CONST CHAR16 *Buffer
1360 )
1361 {
1362 BUFFER_LIST *Node;
1363 BUFFER_LIST *Walker;
1364 UINT16 MaxHistoryCmdCount;
1365 UINT16 Count;
1366
1367 Count = 0;
1368 MaxHistoryCmdCount = PcdGet16(PcdShellMaxHistoryCommandCount);
1369
1370 if (MaxHistoryCmdCount == 0) {
1371 return ;
1372 }
1373
1374
1375 Node = AllocateZeroPool(sizeof(BUFFER_LIST));
1376 ASSERT(Node != NULL);
1377 Node->Buffer = AllocateCopyPool(StrSize(Buffer), Buffer);
1378 ASSERT(Node->Buffer != NULL);
1379
1380 for ( Walker = (BUFFER_LIST*)GetFirstNode(&ShellInfoObject.ViewingSettings.CommandHistory.Link)
1381 ; !IsNull(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Walker->Link)
1382 ; Walker = (BUFFER_LIST*)GetNextNode(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Walker->Link)
1383 ){
1384 Count++;
1385 }
1386 if (Count < MaxHistoryCmdCount){
1387 InsertTailList(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link);
1388 } else {
1389 Walker = (BUFFER_LIST*)GetFirstNode(&ShellInfoObject.ViewingSettings.CommandHistory.Link);
1390 RemoveEntryList(&Walker->Link);
1391 if (Walker->Buffer != NULL) {
1392 FreePool(Walker->Buffer);
1393 }
1394 FreePool(Walker);
1395 InsertTailList(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link);
1396 }
1397 }
1398
1399 /**
1400 Checks if a string is an alias for another command. If yes, then it replaces the alias name
1401 with the correct command name.
1402
1403 @param[in, out] CommandString Upon entry the potential alias. Upon return the
1404 command name if it was an alias. If it was not
1405 an alias it will be unchanged. This function may
1406 change the buffer to fit the command name.
1407
1408 @retval EFI_SUCCESS The name was changed.
1409 @retval EFI_SUCCESS The name was not an alias.
1410 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
1411 **/
1412 EFI_STATUS
1413 EFIAPI
1414 ShellConvertAlias(
1415 IN OUT CHAR16 **CommandString
1416 )
1417 {
1418 CONST CHAR16 *NewString;
1419
1420 NewString = ShellInfoObject.NewEfiShellProtocol->GetAlias(*CommandString, NULL);
1421 if (NewString == NULL) {
1422 return (EFI_SUCCESS);
1423 }
1424 FreePool(*CommandString);
1425 *CommandString = AllocateCopyPool(StrSize(NewString), NewString);
1426 if (*CommandString == NULL) {
1427 return (EFI_OUT_OF_RESOURCES);
1428 }
1429 return (EFI_SUCCESS);
1430 }
1431
1432 /**
1433 This function will eliminate unreplaced (and therefore non-found) environment variables.
1434
1435 @param[in,out] CmdLine The command line to update.
1436 **/
1437 EFI_STATUS
1438 EFIAPI
1439 StripUnreplacedEnvironmentVariables(
1440 IN OUT CHAR16 *CmdLine
1441 )
1442 {
1443 CHAR16 *FirstPercent;
1444 CHAR16 *FirstQuote;
1445 CHAR16 *SecondPercent;
1446 CHAR16 *SecondQuote;
1447 CHAR16 *CurrentLocator;
1448
1449 for (CurrentLocator = CmdLine ; CurrentLocator != NULL ; ) {
1450 FirstQuote = FindNextInstance(CurrentLocator, L"\"", TRUE);
1451 FirstPercent = FindNextInstance(CurrentLocator, L"%", TRUE);
1452 SecondPercent = FirstPercent!=NULL?FindNextInstance(FirstPercent+1, L"%", TRUE):NULL;
1453 if (FirstPercent == NULL || SecondPercent == NULL) {
1454 //
1455 // If we ever don't have 2 % we are done.
1456 //
1457 break;
1458 }
1459
1460 if (FirstQuote!= NULL && FirstQuote < FirstPercent) {
1461 SecondQuote = FindNextInstance(FirstQuote+1, L"\"", TRUE);
1462 //
1463 // Quote is first found
1464 //
1465
1466 if (SecondQuote < FirstPercent) {
1467 //
1468 // restart after the pair of "
1469 //
1470 CurrentLocator = SecondQuote + 1;
1471 } else /* FirstPercent < SecondQuote */{
1472 //
1473 // Restart on the first percent
1474 //
1475 CurrentLocator = FirstPercent;
1476 }
1477 continue;
1478 }
1479
1480 if (FirstQuote == NULL || SecondPercent < FirstQuote) {
1481 if (IsValidEnvironmentVariableName(FirstPercent, SecondPercent)) {
1482 //
1483 // We need to remove from FirstPercent to SecondPercent
1484 //
1485 CopyMem(FirstPercent, SecondPercent + 1, StrSize(SecondPercent + 1));
1486 //
1487 // don't need to update the locator. both % characters are gone.
1488 //
1489 } else {
1490 CurrentLocator = SecondPercent + 1;
1491 }
1492 continue;
1493 }
1494 CurrentLocator = FirstQuote;
1495 }
1496 return (EFI_SUCCESS);
1497 }
1498
1499 /**
1500 Function allocates a new command line and replaces all instances of environment
1501 variable names that are correctly preset to their values.
1502
1503 If the return value is not NULL the memory must be caller freed.
1504
1505 @param[in] OriginalCommandLine The original command line
1506
1507 @retval NULL An error occurred.
1508 @return The new command line with no environment variables present.
1509 **/
1510 CHAR16*
1511 EFIAPI
1512 ShellConvertVariables (
1513 IN CONST CHAR16 *OriginalCommandLine
1514 )
1515 {
1516 CONST CHAR16 *MasterEnvList;
1517 UINTN NewSize;
1518 CHAR16 *NewCommandLine1;
1519 CHAR16 *NewCommandLine2;
1520 CHAR16 *Temp;
1521 UINTN ItemSize;
1522 CHAR16 *ItemTemp;
1523 SCRIPT_FILE *CurrentScriptFile;
1524 ALIAS_LIST *AliasListNode;
1525
1526 ASSERT(OriginalCommandLine != NULL);
1527
1528 ItemSize = 0;
1529 NewSize = StrSize(OriginalCommandLine);
1530 CurrentScriptFile = ShellCommandGetCurrentScriptFile();
1531 Temp = NULL;
1532
1533 ///@todo update this to handle the %0 - %9 for scripting only (borrow from line 1256 area) ? ? ?
1534
1535 //
1536 // calculate the size required for the post-conversion string...
1537 //
1538 if (CurrentScriptFile != NULL) {
1539 for (AliasListNode = (ALIAS_LIST*)GetFirstNode(&CurrentScriptFile->SubstList)
1540 ; !IsNull(&CurrentScriptFile->SubstList, &AliasListNode->Link)
1541 ; AliasListNode = (ALIAS_LIST*)GetNextNode(&CurrentScriptFile->SubstList, &AliasListNode->Link)
1542 ){
1543 for (Temp = StrStr(OriginalCommandLine, AliasListNode->Alias)
1544 ; Temp != NULL
1545 ; Temp = StrStr(Temp+1, AliasListNode->Alias)
1546 ){
1547 //
1548 // we need a preceding and if there is space no ^ preceding (if no space ignore)
1549 //
1550 if ((((Temp-OriginalCommandLine)>2) && *(Temp-2) != L'^') || ((Temp-OriginalCommandLine)<=2)) {
1551 NewSize += StrSize(AliasListNode->CommandString);
1552 }
1553 }
1554 }
1555 }
1556
1557 for (MasterEnvList = EfiShellGetEnv(NULL)
1558 ; MasterEnvList != NULL && *MasterEnvList != CHAR_NULL //&& *(MasterEnvList+1) != CHAR_NULL
1559 ; MasterEnvList += StrLen(MasterEnvList) + 1
1560 ){
1561 if (StrSize(MasterEnvList) > ItemSize) {
1562 ItemSize = StrSize(MasterEnvList);
1563 }
1564 for (Temp = StrStr(OriginalCommandLine, MasterEnvList)
1565 ; Temp != NULL
1566 ; Temp = StrStr(Temp+1, MasterEnvList)
1567 ){
1568 //
1569 // we need a preceding and following % and if there is space no ^ preceding (if no space ignore)
1570 //
1571 if (*(Temp-1) == L'%' && *(Temp+StrLen(MasterEnvList)) == L'%' &&
1572 ((((Temp-OriginalCommandLine)>2) && *(Temp-2) != L'^') || ((Temp-OriginalCommandLine)<=2))) {
1573 NewSize+=StrSize(EfiShellGetEnv(MasterEnvList));
1574 }
1575 }
1576 }
1577
1578 //
1579 // now do the replacements...
1580 //
1581 NewCommandLine1 = AllocateCopyPool(NewSize, OriginalCommandLine);
1582 NewCommandLine2 = AllocateZeroPool(NewSize);
1583 ItemTemp = AllocateZeroPool(ItemSize+(2*sizeof(CHAR16)));
1584 if (NewCommandLine1 == NULL || NewCommandLine2 == NULL || ItemTemp == NULL) {
1585 SHELL_FREE_NON_NULL(NewCommandLine1);
1586 SHELL_FREE_NON_NULL(NewCommandLine2);
1587 SHELL_FREE_NON_NULL(ItemTemp);
1588 return (NULL);
1589 }
1590 for (MasterEnvList = EfiShellGetEnv(NULL)
1591 ; MasterEnvList != NULL && *MasterEnvList != CHAR_NULL
1592 ; MasterEnvList += StrLen(MasterEnvList) + 1
1593 ){
1594 StrCpyS( ItemTemp,
1595 ((ItemSize+(2*sizeof(CHAR16)))/sizeof(CHAR16)),
1596 L"%"
1597 );
1598 StrCatS( ItemTemp,
1599 ((ItemSize+(2*sizeof(CHAR16)))/sizeof(CHAR16)),
1600 MasterEnvList
1601 );
1602 StrCatS( ItemTemp,
1603 ((ItemSize+(2*sizeof(CHAR16)))/sizeof(CHAR16)),
1604 L"%"
1605 );
1606 ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, ItemTemp, EfiShellGetEnv(MasterEnvList), TRUE, FALSE);
1607 StrCpyS(NewCommandLine1, NewSize/sizeof(CHAR16), NewCommandLine2);
1608 }
1609 if (CurrentScriptFile != NULL) {
1610 for (AliasListNode = (ALIAS_LIST*)GetFirstNode(&CurrentScriptFile->SubstList)
1611 ; !IsNull(&CurrentScriptFile->SubstList, &AliasListNode->Link)
1612 ; AliasListNode = (ALIAS_LIST*)GetNextNode(&CurrentScriptFile->SubstList, &AliasListNode->Link)
1613 ){
1614 ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, AliasListNode->Alias, AliasListNode->CommandString, TRUE, FALSE);
1615 StrCpyS(NewCommandLine1, NewSize/sizeof(CHAR16), NewCommandLine2);
1616 }
1617 }
1618
1619 //
1620 // Remove non-existent environment variables
1621 //
1622 StripUnreplacedEnvironmentVariables(NewCommandLine1);
1623
1624 //
1625 // Now cleanup any straggler intentionally ignored "%" characters
1626 //
1627 ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, L"^%", L"%", TRUE, FALSE);
1628 StrCpyS(NewCommandLine1, NewSize/sizeof(CHAR16), NewCommandLine2);
1629
1630 FreePool(NewCommandLine2);
1631 FreePool(ItemTemp);
1632
1633 return (NewCommandLine1);
1634 }
1635
1636 /**
1637 Internal function to run a command line with pipe usage.
1638
1639 @param[in] CmdLine The pointer to the command line.
1640 @param[in] StdIn The pointer to the Standard input.
1641 @param[in] StdOut The pointer to the Standard output.
1642
1643 @retval EFI_SUCCESS The split command is executed successfully.
1644 @retval other Some error occurs when executing the split command.
1645 **/
1646 EFI_STATUS
1647 EFIAPI
1648 RunSplitCommand(
1649 IN CONST CHAR16 *CmdLine,
1650 IN SHELL_FILE_HANDLE *StdIn,
1651 IN SHELL_FILE_HANDLE *StdOut
1652 )
1653 {
1654 EFI_STATUS Status;
1655 CHAR16 *NextCommandLine;
1656 CHAR16 *OurCommandLine;
1657 UINTN Size1;
1658 UINTN Size2;
1659 SPLIT_LIST *Split;
1660 SHELL_FILE_HANDLE *TempFileHandle;
1661 BOOLEAN Unicode;
1662
1663 ASSERT(StdOut == NULL);
1664
1665 ASSERT(StrStr(CmdLine, L"|") != NULL);
1666
1667 Status = EFI_SUCCESS;
1668 NextCommandLine = NULL;
1669 OurCommandLine = NULL;
1670 Size1 = 0;
1671 Size2 = 0;
1672
1673 NextCommandLine = StrnCatGrow(&NextCommandLine, &Size1, StrStr(CmdLine, L"|")+1, 0);
1674 OurCommandLine = StrnCatGrow(&OurCommandLine , &Size2, CmdLine , StrStr(CmdLine, L"|") - CmdLine);
1675
1676 if (NextCommandLine == NULL || OurCommandLine == NULL) {
1677 SHELL_FREE_NON_NULL(OurCommandLine);
1678 SHELL_FREE_NON_NULL(NextCommandLine);
1679 return (EFI_OUT_OF_RESOURCES);
1680 } else if (StrStr(OurCommandLine, L"|") != NULL || Size1 == 0 || Size2 == 0) {
1681 SHELL_FREE_NON_NULL(OurCommandLine);
1682 SHELL_FREE_NON_NULL(NextCommandLine);
1683 return (EFI_INVALID_PARAMETER);
1684 } else if (NextCommandLine[0] == L'a' &&
1685 (NextCommandLine[1] == L' ' || NextCommandLine[1] == CHAR_NULL)
1686 ){
1687 CopyMem(NextCommandLine, NextCommandLine+1, StrSize(NextCommandLine) - sizeof(NextCommandLine[0]));
1688 while (NextCommandLine[0] == L' ') {
1689 CopyMem(NextCommandLine, NextCommandLine+1, StrSize(NextCommandLine) - sizeof(NextCommandLine[0]));
1690 }
1691 if (NextCommandLine[0] == CHAR_NULL) {
1692 SHELL_FREE_NON_NULL(OurCommandLine);
1693 SHELL_FREE_NON_NULL(NextCommandLine);
1694 return (EFI_INVALID_PARAMETER);
1695 }
1696 Unicode = FALSE;
1697 } else {
1698 Unicode = TRUE;
1699 }
1700
1701
1702 //
1703 // make a SPLIT_LIST item and add to list
1704 //
1705 Split = AllocateZeroPool(sizeof(SPLIT_LIST));
1706 ASSERT(Split != NULL);
1707 Split->SplitStdIn = StdIn;
1708 Split->SplitStdOut = ConvertEfiFileProtocolToShellHandle(CreateFileInterfaceMem(Unicode), NULL);
1709 ASSERT(Split->SplitStdOut != NULL);
1710 InsertHeadList(&ShellInfoObject.SplitList.Link, &Split->Link);
1711
1712 Status = RunCommand(OurCommandLine);
1713
1714 //
1715 // move the output from the first to the in to the second.
1716 //
1717 TempFileHandle = Split->SplitStdOut;
1718 if (Split->SplitStdIn == StdIn) {
1719 Split->SplitStdOut = NULL;
1720 } else {
1721 Split->SplitStdOut = Split->SplitStdIn;
1722 }
1723 Split->SplitStdIn = TempFileHandle;
1724 ShellInfoObject.NewEfiShellProtocol->SetFilePosition(ConvertShellHandleToEfiFileProtocol(Split->SplitStdIn), 0);
1725
1726 if (!EFI_ERROR(Status)) {
1727 Status = RunCommand(NextCommandLine);
1728 }
1729
1730 //
1731 // remove the top level from the ScriptList
1732 //
1733 ASSERT((SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link) == Split);
1734 RemoveEntryList(&Split->Link);
1735
1736 //
1737 // Note that the original StdIn is now the StdOut...
1738 //
1739 if (Split->SplitStdOut != NULL && Split->SplitStdOut != StdIn) {
1740 ShellInfoObject.NewEfiShellProtocol->CloseFile(ConvertShellHandleToEfiFileProtocol(Split->SplitStdOut));
1741 }
1742 if (Split->SplitStdIn != NULL) {
1743 ShellInfoObject.NewEfiShellProtocol->CloseFile(ConvertShellHandleToEfiFileProtocol(Split->SplitStdIn));
1744 }
1745
1746 FreePool(Split);
1747 FreePool(NextCommandLine);
1748 FreePool(OurCommandLine);
1749
1750 return (Status);
1751 }
1752
1753 /**
1754 Take the original command line, substitute any variables, free
1755 the original string, return the modified copy.
1756
1757 @param[in] CmdLine pointer to the command line to update.
1758
1759 @retval EFI_SUCCESS the function was successful.
1760 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
1761 **/
1762 EFI_STATUS
1763 EFIAPI
1764 ShellSubstituteVariables(
1765 IN CHAR16 **CmdLine
1766 )
1767 {
1768 CHAR16 *NewCmdLine;
1769 NewCmdLine = ShellConvertVariables(*CmdLine);
1770 SHELL_FREE_NON_NULL(*CmdLine);
1771 if (NewCmdLine == NULL) {
1772 return (EFI_OUT_OF_RESOURCES);
1773 }
1774 *CmdLine = NewCmdLine;
1775 return (EFI_SUCCESS);
1776 }
1777
1778 /**
1779 Take the original command line, substitute any alias in the first group of space delimited characters, free
1780 the original string, return the modified copy.
1781
1782 @param[in] CmdLine pointer to the command line to update.
1783
1784 @retval EFI_SUCCESS the function was successful.
1785 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
1786 **/
1787 EFI_STATUS
1788 EFIAPI
1789 ShellSubstituteAliases(
1790 IN CHAR16 **CmdLine
1791 )
1792 {
1793 CHAR16 *NewCmdLine;
1794 CHAR16 *CommandName;
1795 EFI_STATUS Status;
1796 UINTN PostAliasSize;
1797 ASSERT(CmdLine != NULL);
1798 ASSERT(*CmdLine!= NULL);
1799
1800
1801 CommandName = NULL;
1802 if (StrStr((*CmdLine), L" ") == NULL){
1803 StrnCatGrow(&CommandName, NULL, (*CmdLine), 0);
1804 } else {
1805 StrnCatGrow(&CommandName, NULL, (*CmdLine), StrStr((*CmdLine), L" ") - (*CmdLine));
1806 }
1807
1808 //
1809 // This cannot happen 'inline' since the CmdLine can need extra space.
1810 //
1811 NewCmdLine = NULL;
1812 if (!ShellCommandIsCommandOnList(CommandName)) {
1813 //
1814 // Convert via alias
1815 //
1816 Status = ShellConvertAlias(&CommandName);
1817 if (EFI_ERROR(Status)){
1818 return (Status);
1819 }
1820 PostAliasSize = 0;
1821 NewCmdLine = StrnCatGrow(&NewCmdLine, &PostAliasSize, CommandName, 0);
1822 if (NewCmdLine == NULL) {
1823 SHELL_FREE_NON_NULL(CommandName);
1824 SHELL_FREE_NON_NULL(*CmdLine);
1825 return (EFI_OUT_OF_RESOURCES);
1826 }
1827 NewCmdLine = StrnCatGrow(&NewCmdLine, &PostAliasSize, StrStr((*CmdLine), L" "), 0);
1828 if (NewCmdLine == NULL) {
1829 SHELL_FREE_NON_NULL(CommandName);
1830 SHELL_FREE_NON_NULL(*CmdLine);
1831 return (EFI_OUT_OF_RESOURCES);
1832 }
1833 } else {
1834 NewCmdLine = StrnCatGrow(&NewCmdLine, NULL, (*CmdLine), 0);
1835 }
1836
1837 SHELL_FREE_NON_NULL(*CmdLine);
1838 SHELL_FREE_NON_NULL(CommandName);
1839
1840 //
1841 // re-assign the passed in double pointer to point to our newly allocated buffer
1842 //
1843 *CmdLine = NewCmdLine;
1844
1845 return (EFI_SUCCESS);
1846 }
1847
1848 /**
1849 Takes the Argv[0] part of the command line and determine the meaning of it.
1850
1851 @param[in] CmdName pointer to the command line to update.
1852
1853 @retval Internal_Command The name is an internal command.
1854 @retval File_Sys_Change the name is a file system change.
1855 @retval Script_File_Name the name is a NSH script file.
1856 @retval Unknown_Invalid the name is unknown.
1857 @retval Efi_Application the name is an application (.EFI).
1858 **/
1859 SHELL_OPERATION_TYPES
1860 EFIAPI
1861 GetOperationType(
1862 IN CONST CHAR16 *CmdName
1863 )
1864 {
1865 CHAR16* FileWithPath;
1866 CONST CHAR16* TempLocation;
1867 CONST CHAR16* TempLocation2;
1868
1869 FileWithPath = NULL;
1870 //
1871 // test for an internal command.
1872 //
1873 if (ShellCommandIsCommandOnList(CmdName)) {
1874 return (Internal_Command);
1875 }
1876
1877 //
1878 // Test for file system change request. anything ending with first : and cant have spaces.
1879 //
1880 if (CmdName[(StrLen(CmdName)-1)] == L':') {
1881 if ( StrStr(CmdName, L" ") != NULL
1882 || StrLen(StrStr(CmdName, L":")) > 1
1883 ) {
1884 return (Unknown_Invalid);
1885 }
1886 return (File_Sys_Change);
1887 }
1888
1889 //
1890 // Test for a file
1891 //
1892 if ((FileWithPath = ShellFindFilePathEx(CmdName, mExecutableExtensions)) != NULL) {
1893 //
1894 // See if that file has a script file extension
1895 //
1896 if (StrLen(FileWithPath) > 4) {
1897 TempLocation = FileWithPath+StrLen(FileWithPath)-4;
1898 TempLocation2 = mScriptExtension;
1899 if (StringNoCaseCompare((VOID*)(&TempLocation), (VOID*)(&TempLocation2)) == 0) {
1900 SHELL_FREE_NON_NULL(FileWithPath);
1901 return (Script_File_Name);
1902 }
1903 }
1904
1905 //
1906 // Was a file, but not a script. we treat this as an application.
1907 //
1908 SHELL_FREE_NON_NULL(FileWithPath);
1909 return (Efi_Application);
1910 }
1911
1912 SHELL_FREE_NON_NULL(FileWithPath);
1913 //
1914 // No clue what this is... return invalid flag...
1915 //
1916 return (Unknown_Invalid);
1917 }
1918
1919 /**
1920 Determine if the first item in a command line is valid.
1921
1922 @param[in] CmdLine The command line to parse.
1923
1924 @retval EFI_SUCCESS The item is valid.
1925 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
1926 @retval EFI_NOT_FOUND The operation type is unknown or invalid.
1927 **/
1928 EFI_STATUS
1929 EFIAPI
1930 IsValidSplit(
1931 IN CONST CHAR16 *CmdLine
1932 )
1933 {
1934 CHAR16 *Temp;
1935 CHAR16 *FirstParameter;
1936 CHAR16 *TempWalker;
1937 EFI_STATUS Status;
1938
1939 Temp = NULL;
1940
1941 Temp = StrnCatGrow(&Temp, NULL, CmdLine, 0);
1942 if (Temp == NULL) {
1943 return (EFI_OUT_OF_RESOURCES);
1944 }
1945
1946 FirstParameter = StrStr(Temp, L"|");
1947 if (FirstParameter != NULL) {
1948 *FirstParameter = CHAR_NULL;
1949 }
1950
1951 FirstParameter = NULL;
1952
1953 //
1954 // Process the command line
1955 //
1956 Status = ProcessCommandLineToFinal(&Temp);
1957
1958 if (!EFI_ERROR(Status)) {
1959 FirstParameter = AllocateZeroPool(StrSize(CmdLine));
1960 if (FirstParameter == NULL) {
1961 SHELL_FREE_NON_NULL(Temp);
1962 return (EFI_OUT_OF_RESOURCES);
1963 }
1964 TempWalker = (CHAR16*)Temp;
1965 if (!EFI_ERROR(GetNextParameter(&TempWalker, &FirstParameter, StrSize(CmdLine), TRUE))) {
1966 if (GetOperationType(FirstParameter) == Unknown_Invalid) {
1967 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);
1968 SetLastError(SHELL_NOT_FOUND);
1969 Status = EFI_NOT_FOUND;
1970 }
1971 }
1972 }
1973
1974 SHELL_FREE_NON_NULL(Temp);
1975 SHELL_FREE_NON_NULL(FirstParameter);
1976 return Status;
1977 }
1978
1979 /**
1980 Determine if a command line contains with a split contains only valid commands.
1981
1982 @param[in] CmdLine The command line to parse.
1983
1984 @retval EFI_SUCCESS CmdLine has only valid commands, application, or has no split.
1985 @retval EFI_ABORTED CmdLine has at least one invalid command or application.
1986 **/
1987 EFI_STATUS
1988 EFIAPI
1989 VerifySplit(
1990 IN CONST CHAR16 *CmdLine
1991 )
1992 {
1993 CONST CHAR16 *TempSpot;
1994 EFI_STATUS Status;
1995
1996 //
1997 // If this was the only item, then get out
1998 //
1999 if (!ContainsSplit(CmdLine)) {
2000 return (EFI_SUCCESS);
2001 }
2002
2003 //
2004 // Verify up to the pipe or end character
2005 //
2006 Status = IsValidSplit(CmdLine);
2007 if (EFI_ERROR(Status)) {
2008 return (Status);
2009 }
2010
2011 //
2012 // recurse to verify the next item
2013 //
2014 TempSpot = FindFirstCharacter(CmdLine, L"|", L'^') + 1;
2015 if (*TempSpot == L'a' &&
2016 (*(TempSpot + 1) == L' ' || *(TempSpot + 1) == CHAR_NULL)
2017 ) {
2018 // If it's an ASCII pipe '|a'
2019 TempSpot += 1;
2020 }
2021
2022 return (VerifySplit(TempSpot));
2023 }
2024
2025 /**
2026 Process a split based operation.
2027
2028 @param[in] CmdLine pointer to the command line to process
2029
2030 @retval EFI_SUCCESS The operation was successful
2031 @return an error occurred.
2032 **/
2033 EFI_STATUS
2034 EFIAPI
2035 ProcessNewSplitCommandLine(
2036 IN CONST CHAR16 *CmdLine
2037 )
2038 {
2039 SPLIT_LIST *Split;
2040 EFI_STATUS Status;
2041
2042 Status = VerifySplit(CmdLine);
2043 if (EFI_ERROR(Status)) {
2044 return (Status);
2045 }
2046
2047 Split = NULL;
2048
2049 //
2050 // are we in an existing split???
2051 //
2052 if (!IsListEmpty(&ShellInfoObject.SplitList.Link)) {
2053 Split = (SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link);
2054 }
2055
2056 if (Split == NULL) {
2057 Status = RunSplitCommand(CmdLine, NULL, NULL);
2058 } else {
2059 Status = RunSplitCommand(CmdLine, Split->SplitStdIn, Split->SplitStdOut);
2060 }
2061 if (EFI_ERROR(Status)) {
2062 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_SPLIT), ShellInfoObject.HiiHandle, CmdLine);
2063 }
2064 return (Status);
2065 }
2066
2067 /**
2068 Handle a request to change the current file system.
2069
2070 @param[in] CmdLine The passed in command line.
2071
2072 @retval EFI_SUCCESS The operation was successful.
2073 **/
2074 EFI_STATUS
2075 EFIAPI
2076 ChangeMappedDrive(
2077 IN CONST CHAR16 *CmdLine
2078 )
2079 {
2080 EFI_STATUS Status;
2081 Status = EFI_SUCCESS;
2082
2083 //
2084 // make sure we are the right operation
2085 //
2086 ASSERT(CmdLine[(StrLen(CmdLine)-1)] == L':' && StrStr(CmdLine, L" ") == NULL);
2087
2088 //
2089 // Call the protocol API to do the work
2090 //
2091 Status = ShellInfoObject.NewEfiShellProtocol->SetCurDir(NULL, CmdLine);
2092
2093 //
2094 // Report any errors
2095 //
2096 if (EFI_ERROR(Status)) {
2097 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_MAPPING), ShellInfoObject.HiiHandle, CmdLine);
2098 }
2099
2100 return (Status);
2101 }
2102
2103 /**
2104 Reprocess the command line to direct all -? to the help command.
2105
2106 if found, will add "help" as argv[0], and move the rest later.
2107
2108 @param[in,out] CmdLine pointer to the command line to update
2109 **/
2110 EFI_STATUS
2111 EFIAPI
2112 DoHelpUpdate(
2113 IN OUT CHAR16 **CmdLine
2114 )
2115 {
2116 CHAR16 *CurrentParameter;
2117 CHAR16 *Walker;
2118 CHAR16 *NewCommandLine;
2119 EFI_STATUS Status;
2120 UINTN NewCmdLineSize;
2121
2122 Status = EFI_SUCCESS;
2123
2124 CurrentParameter = AllocateZeroPool(StrSize(*CmdLine));
2125 if (CurrentParameter == NULL) {
2126 return (EFI_OUT_OF_RESOURCES);
2127 }
2128
2129 Walker = *CmdLine;
2130 while(Walker != NULL && *Walker != CHAR_NULL) {
2131 if (!EFI_ERROR(GetNextParameter(&Walker, &CurrentParameter, StrSize(*CmdLine), TRUE))) {
2132 if (StrStr(CurrentParameter, L"-?") == CurrentParameter) {
2133 CurrentParameter[0] = L' ';
2134 CurrentParameter[1] = L' ';
2135 NewCmdLineSize = StrSize(L"help ") + StrSize(*CmdLine);
2136 NewCommandLine = AllocateZeroPool(NewCmdLineSize);
2137 if (NewCommandLine == NULL) {
2138 Status = EFI_OUT_OF_RESOURCES;
2139 break;
2140 }
2141
2142 //
2143 // We know the space is sufficient since we just calculated it.
2144 //
2145 StrnCpyS(NewCommandLine, NewCmdLineSize/sizeof(CHAR16), L"help ", 5);
2146 StrnCatS(NewCommandLine, NewCmdLineSize/sizeof(CHAR16), *CmdLine, StrLen(*CmdLine));
2147 SHELL_FREE_NON_NULL(*CmdLine);
2148 *CmdLine = NewCommandLine;
2149 break;
2150 }
2151 }
2152 }
2153
2154 SHELL_FREE_NON_NULL(CurrentParameter);
2155
2156 return (Status);
2157 }
2158
2159 /**
2160 Function to update the shell variable "lasterror".
2161
2162 @param[in] ErrorCode the error code to put into lasterror.
2163 **/
2164 EFI_STATUS
2165 EFIAPI
2166 SetLastError(
2167 IN CONST SHELL_STATUS ErrorCode
2168 )
2169 {
2170 CHAR16 LeString[19];
2171 if (sizeof(EFI_STATUS) == sizeof(UINT64)) {
2172 UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", ErrorCode);
2173 } else {
2174 UnicodeSPrint(LeString, sizeof(LeString), L"0x%x", ErrorCode);
2175 }
2176 DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE););
2177 InternalEfiShellSetEnv(L"lasterror", LeString, TRUE);
2178
2179 return (EFI_SUCCESS);
2180 }
2181
2182 /**
2183 Converts the command line to it's post-processed form. this replaces variables and alias' per UEFI Shell spec.
2184
2185 @param[in,out] CmdLine pointer to the command line to update
2186
2187 @retval EFI_SUCCESS The operation was successful
2188 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
2189 @return some other error occurred
2190 **/
2191 EFI_STATUS
2192 EFIAPI
2193 ProcessCommandLineToFinal(
2194 IN OUT CHAR16 **CmdLine
2195 )
2196 {
2197 EFI_STATUS Status;
2198 TrimSpaces(CmdLine);
2199
2200 Status = ShellSubstituteAliases(CmdLine);
2201 if (EFI_ERROR(Status)) {
2202 return (Status);
2203 }
2204
2205 TrimSpaces(CmdLine);
2206
2207 Status = ShellSubstituteVariables(CmdLine);
2208 if (EFI_ERROR(Status)) {
2209 return (Status);
2210 }
2211 ASSERT (*CmdLine != NULL);
2212
2213 TrimSpaces(CmdLine);
2214
2215 //
2216 // update for help parsing
2217 //
2218 if (StrStr(*CmdLine, L"?") != NULL) {
2219 //
2220 // This may do nothing if the ? does not indicate help.
2221 // Save all the details for in the API below.
2222 //
2223 Status = DoHelpUpdate(CmdLine);
2224 }
2225
2226 TrimSpaces(CmdLine);
2227
2228 return (EFI_SUCCESS);
2229 }
2230
2231 /**
2232 Run an internal shell command.
2233
2234 This API will update the shell's environment since these commands are libraries.
2235
2236 @param[in] CmdLine the command line to run.
2237 @param[in] FirstParameter the first parameter on the command line
2238 @param[in] ParamProtocol the shell parameters protocol pointer
2239 @param[out] CommandStatus the status from the command line.
2240
2241 @retval EFI_SUCCESS The command was completed.
2242 @retval EFI_ABORTED The command's operation was aborted.
2243 **/
2244 EFI_STATUS
2245 EFIAPI
2246 RunInternalCommand(
2247 IN CONST CHAR16 *CmdLine,
2248 IN CHAR16 *FirstParameter,
2249 IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol,
2250 OUT EFI_STATUS *CommandStatus
2251 )
2252 {
2253 EFI_STATUS Status;
2254 UINTN Argc;
2255 CHAR16 **Argv;
2256 SHELL_STATUS CommandReturnedStatus;
2257 BOOLEAN LastError;
2258 CHAR16 *Walker;
2259 CHAR16 *NewCmdLine;
2260
2261 NewCmdLine = AllocateCopyPool (StrSize (CmdLine), CmdLine);
2262 if (NewCmdLine == NULL) {
2263 return EFI_OUT_OF_RESOURCES;
2264 }
2265
2266 for (Walker = NewCmdLine; Walker != NULL && *Walker != CHAR_NULL ; Walker++) {
2267 if (*Walker == L'^' && *(Walker+1) == L'#') {
2268 CopyMem(Walker, Walker+1, StrSize(Walker) - sizeof(Walker[0]));
2269 }
2270 }
2271
2272 //
2273 // get the argc and argv updated for internal commands
2274 //
2275 Status = UpdateArgcArgv(ParamProtocol, NewCmdLine, Internal_Command, &Argv, &Argc);
2276 if (!EFI_ERROR(Status)) {
2277 //
2278 // Run the internal command.
2279 //
2280 Status = ShellCommandRunCommandHandler(FirstParameter, &CommandReturnedStatus, &LastError);
2281
2282 if (!EFI_ERROR(Status)) {
2283 if (CommandStatus != NULL) {
2284 if (CommandReturnedStatus != SHELL_SUCCESS) {
2285 *CommandStatus = (EFI_STATUS)(CommandReturnedStatus | MAX_BIT);
2286 } else {
2287 *CommandStatus = EFI_SUCCESS;
2288 }
2289 }
2290
2291 //
2292 // Update last error status.
2293 // some commands do not update last error.
2294 //
2295 if (LastError) {
2296 SetLastError(CommandReturnedStatus);
2297 }
2298
2299 //
2300 // Pass thru the exitcode from the app.
2301 //
2302 if (ShellCommandGetExit()) {
2303 //
2304 // An Exit was requested ("exit" command), pass its value up.
2305 //
2306 Status = CommandReturnedStatus;
2307 } else if (CommandReturnedStatus != SHELL_SUCCESS && IsScriptOnlyCommand(FirstParameter)) {
2308 //
2309 // Always abort when a script only command fails for any reason
2310 //
2311 Status = EFI_ABORTED;
2312 } else if (ShellCommandGetCurrentScriptFile() != NULL && CommandReturnedStatus == SHELL_ABORTED) {
2313 //
2314 // Abort when in a script and a command aborted
2315 //
2316 Status = EFI_ABORTED;
2317 }
2318 }
2319 }
2320
2321 //
2322 // This is guaranteed to be called after UpdateArgcArgv no matter what else happened.
2323 // This is safe even if the update API failed. In this case, it may be a no-op.
2324 //
2325 RestoreArgcArgv(ParamProtocol, &Argv, &Argc);
2326
2327 //
2328 // If a script is running and the command is not a script only command, then
2329 // change return value to success so the script won't halt (unless aborted).
2330 //
2331 // Script only commands have to be able halt the script since the script will
2332 // not operate if they are failing.
2333 //
2334 if ( ShellCommandGetCurrentScriptFile() != NULL
2335 && !IsScriptOnlyCommand(FirstParameter)
2336 && Status != EFI_ABORTED
2337 ) {
2338 Status = EFI_SUCCESS;
2339 }
2340
2341 FreePool (NewCmdLine);
2342 return (Status);
2343 }
2344
2345 /**
2346 Function to run the command or file.
2347
2348 @param[in] Type the type of operation being run.
2349 @param[in] CmdLine the command line to run.
2350 @param[in] FirstParameter the first parameter on the command line
2351 @param[in] ParamProtocol the shell parameters protocol pointer
2352 @param[out] CommandStatus the status from the command line.
2353
2354 @retval EFI_SUCCESS The command was completed.
2355 @retval EFI_ABORTED The command's operation was aborted.
2356 **/
2357 EFI_STATUS
2358 EFIAPI
2359 RunCommandOrFile(
2360 IN SHELL_OPERATION_TYPES Type,
2361 IN CONST CHAR16 *CmdLine,
2362 IN CHAR16 *FirstParameter,
2363 IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol,
2364 OUT EFI_STATUS *CommandStatus
2365 )
2366 {
2367 EFI_STATUS Status;
2368 EFI_STATUS StartStatus;
2369 CHAR16 *CommandWithPath;
2370 EFI_DEVICE_PATH_PROTOCOL *DevPath;
2371 SHELL_STATUS CalleeExitStatus;
2372
2373 Status = EFI_SUCCESS;
2374 CommandWithPath = NULL;
2375 DevPath = NULL;
2376 CalleeExitStatus = SHELL_INVALID_PARAMETER;
2377
2378 switch (Type) {
2379 case Internal_Command:
2380 Status = RunInternalCommand(CmdLine, FirstParameter, ParamProtocol, CommandStatus);
2381 break;
2382 case Script_File_Name:
2383 case Efi_Application:
2384 //
2385 // Process a fully qualified path
2386 //
2387 if (StrStr(FirstParameter, L":") != NULL) {
2388 ASSERT (CommandWithPath == NULL);
2389 if (ShellIsFile(FirstParameter) == EFI_SUCCESS) {
2390 CommandWithPath = StrnCatGrow(&CommandWithPath, NULL, FirstParameter, 0);
2391 }
2392 }
2393
2394 //
2395 // Process a relative path and also check in the path environment variable
2396 //
2397 if (CommandWithPath == NULL) {
2398 CommandWithPath = ShellFindFilePathEx(FirstParameter, mExecutableExtensions);
2399 }
2400
2401 //
2402 // This should be impossible now.
2403 //
2404 ASSERT(CommandWithPath != NULL);
2405
2406 //
2407 // Make sure that path is not just a directory (or not found)
2408 //
2409 if (!EFI_ERROR(ShellIsDirectory(CommandWithPath))) {
2410 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);
2411 SetLastError(SHELL_NOT_FOUND);
2412 }
2413 switch (Type) {
2414 case Script_File_Name:
2415 Status = RunScriptFile (CommandWithPath, NULL, CmdLine, ParamProtocol);
2416 break;
2417 case Efi_Application:
2418 //
2419 // Get the device path of the application image
2420 //
2421 DevPath = ShellInfoObject.NewEfiShellProtocol->GetDevicePathFromFilePath(CommandWithPath);
2422 if (DevPath == NULL){
2423 Status = EFI_OUT_OF_RESOURCES;
2424 break;
2425 }
2426
2427 //
2428 // Execute the device path
2429 //
2430 Status = InternalShellExecuteDevicePath(
2431 &gImageHandle,
2432 DevPath,
2433 CmdLine,
2434 NULL,
2435 &StartStatus
2436 );
2437
2438 SHELL_FREE_NON_NULL(DevPath);
2439
2440 if(EFI_ERROR (Status)) {
2441 CalleeExitStatus = (SHELL_STATUS) (Status & (~MAX_BIT));
2442 } else {
2443 CalleeExitStatus = (SHELL_STATUS) StartStatus;
2444 }
2445
2446 if (CommandStatus != NULL) {
2447 *CommandStatus = CalleeExitStatus;
2448 }
2449
2450 //
2451 // Update last error status.
2452 //
2453 // Status is an EFI_STATUS. Clear top bit to convert to SHELL_STATUS
2454 SetLastError(CalleeExitStatus);
2455 break;
2456 default:
2457 //
2458 // Do nothing.
2459 //
2460 break;
2461 }
2462 break;
2463 default:
2464 //
2465 // Do nothing.
2466 //
2467 break;
2468 }
2469
2470 SHELL_FREE_NON_NULL(CommandWithPath);
2471
2472 return (Status);
2473 }
2474
2475 /**
2476 Function to setup StdIn, StdErr, StdOut, and then run the command or file.
2477
2478 @param[in] Type the type of operation being run.
2479 @param[in] CmdLine the command line to run.
2480 @param[in] FirstParameter the first parameter on the command line.
2481 @param[in] ParamProtocol the shell parameters protocol pointer
2482 @param[out] CommandStatus the status from the command line.
2483
2484 @retval EFI_SUCCESS The command was completed.
2485 @retval EFI_ABORTED The command's operation was aborted.
2486 **/
2487 EFI_STATUS
2488 EFIAPI
2489 SetupAndRunCommandOrFile(
2490 IN SHELL_OPERATION_TYPES Type,
2491 IN CHAR16 *CmdLine,
2492 IN CHAR16 *FirstParameter,
2493 IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol,
2494 OUT EFI_STATUS *CommandStatus
2495 )
2496 {
2497 EFI_STATUS Status;
2498 SHELL_FILE_HANDLE OriginalStdIn;
2499 SHELL_FILE_HANDLE OriginalStdOut;
2500 SHELL_FILE_HANDLE OriginalStdErr;
2501 SYSTEM_TABLE_INFO OriginalSystemTableInfo;
2502
2503 //
2504 // Update the StdIn, StdOut, and StdErr for redirection to environment variables, files, etc... unicode and ASCII
2505 //
2506 Status = UpdateStdInStdOutStdErr(ParamProtocol, CmdLine, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);
2507
2508 //
2509 // The StdIn, StdOut, and StdErr are set up.
2510 // Now run the command, script, or application
2511 //
2512 if (!EFI_ERROR(Status)) {
2513 TrimSpaces(&CmdLine);
2514 Status = RunCommandOrFile(Type, CmdLine, FirstParameter, ParamProtocol, CommandStatus);
2515 }
2516
2517 //
2518 // Now print errors
2519 //
2520 if (EFI_ERROR(Status)) {
2521 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_ERROR), ShellInfoObject.HiiHandle, (VOID*)(Status));
2522 }
2523
2524 //
2525 // put back the original StdIn, StdOut, and StdErr
2526 //
2527 RestoreStdInStdOutStdErr(ParamProtocol, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);
2528
2529 return (Status);
2530 }
2531
2532 /**
2533 Function will process and run a command line.
2534
2535 This will determine if the command line represents an internal shell
2536 command or dispatch an external application.
2537
2538 @param[in] CmdLine The command line to parse.
2539 @param[out] CommandStatus The status from the command line.
2540
2541 @retval EFI_SUCCESS The command was completed.
2542 @retval EFI_ABORTED The command's operation was aborted.
2543 **/
2544 EFI_STATUS
2545 EFIAPI
2546 RunShellCommand(
2547 IN CONST CHAR16 *CmdLine,
2548 OUT EFI_STATUS *CommandStatus
2549 )
2550 {
2551 EFI_STATUS Status;
2552 CHAR16 *CleanOriginal;
2553 CHAR16 *FirstParameter;
2554 CHAR16 *TempWalker;
2555 SHELL_OPERATION_TYPES Type;
2556
2557 ASSERT(CmdLine != NULL);
2558 if (StrLen(CmdLine) == 0) {
2559 return (EFI_SUCCESS);
2560 }
2561
2562 Status = EFI_SUCCESS;
2563 CleanOriginal = NULL;
2564
2565 CleanOriginal = StrnCatGrow(&CleanOriginal, NULL, CmdLine, 0);
2566 if (CleanOriginal == NULL) {
2567 return (EFI_OUT_OF_RESOURCES);
2568 }
2569
2570 TrimSpaces(&CleanOriginal);
2571
2572 //
2573 // NULL out comments (leveraged from RunScriptFileHandle() ).
2574 // The # character on a line is used to denote that all characters on the same line
2575 // and to the right of the # are to be ignored by the shell.
2576 // Afterwards, again remove spaces, in case any were between the last command-parameter and '#'.
2577 //
2578 for (TempWalker = CleanOriginal; TempWalker != NULL && *TempWalker != CHAR_NULL; TempWalker++) {
2579 if (*TempWalker == L'^') {
2580 if (*(TempWalker + 1) == L'#') {
2581 TempWalker++;
2582 }
2583 } else if (*TempWalker == L'#') {
2584 *TempWalker = CHAR_NULL;
2585 }
2586 }
2587
2588 TrimSpaces(&CleanOriginal);
2589
2590 //
2591 // Handle case that passed in command line is just 1 or more " " characters.
2592 //
2593 if (StrLen (CleanOriginal) == 0) {
2594 SHELL_FREE_NON_NULL(CleanOriginal);
2595 return (EFI_SUCCESS);
2596 }
2597
2598 Status = ProcessCommandLineToFinal(&CleanOriginal);
2599 if (EFI_ERROR(Status)) {
2600 SHELL_FREE_NON_NULL(CleanOriginal);
2601 return (Status);
2602 }
2603
2604 //
2605 // We don't do normal processing with a split command line (output from one command input to another)
2606 //
2607 if (ContainsSplit(CleanOriginal)) {
2608 Status = ProcessNewSplitCommandLine(CleanOriginal);
2609 SHELL_FREE_NON_NULL(CleanOriginal);
2610 return (Status);
2611 }
2612
2613 //
2614 // We need the first parameter information so we can determine the operation type
2615 //
2616 FirstParameter = AllocateZeroPool(StrSize(CleanOriginal));
2617 if (FirstParameter == NULL) {
2618 SHELL_FREE_NON_NULL(CleanOriginal);
2619 return (EFI_OUT_OF_RESOURCES);
2620 }
2621 TempWalker = CleanOriginal;
2622 if (!EFI_ERROR(GetNextParameter(&TempWalker, &FirstParameter, StrSize(CleanOriginal), TRUE))) {
2623 //
2624 // Depending on the first parameter we change the behavior
2625 //
2626 switch (Type = GetOperationType(FirstParameter)) {
2627 case File_Sys_Change:
2628 Status = ChangeMappedDrive (FirstParameter);
2629 break;
2630 case Internal_Command:
2631 case Script_File_Name:
2632 case Efi_Application:
2633 Status = SetupAndRunCommandOrFile(Type, CleanOriginal, FirstParameter, ShellInfoObject.NewShellParametersProtocol, CommandStatus);
2634 break;
2635 default:
2636 //
2637 // Whatever was typed, it was invalid.
2638 //
2639 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);
2640 SetLastError(SHELL_NOT_FOUND);
2641 break;
2642 }
2643 } else {
2644 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);
2645 SetLastError(SHELL_NOT_FOUND);
2646 }
2647
2648 SHELL_FREE_NON_NULL(CleanOriginal);
2649 SHELL_FREE_NON_NULL(FirstParameter);
2650
2651 return (Status);
2652 }
2653
2654 /**
2655 Function will process and run a command line.
2656
2657 This will determine if the command line represents an internal shell
2658 command or dispatch an external application.
2659
2660 @param[in] CmdLine The command line to parse.
2661
2662 @retval EFI_SUCCESS The command was completed.
2663 @retval EFI_ABORTED The command's operation was aborted.
2664 **/
2665 EFI_STATUS
2666 EFIAPI
2667 RunCommand(
2668 IN CONST CHAR16 *CmdLine
2669 )
2670 {
2671 return (RunShellCommand(CmdLine, NULL));
2672 }
2673
2674
2675 STATIC CONST UINT16 InvalidChars[] = {L'*', L'?', L'<', L'>', L'\\', L'/', L'\"', 0x0001, 0x0002};
2676 /**
2677 Function determines if the CommandName COULD be a valid command. It does not determine whether
2678 this is a valid command. It only checks for invalid characters.
2679
2680 @param[in] CommandName The name to check
2681
2682 @retval TRUE CommandName could be a command name
2683 @retval FALSE CommandName could not be a valid command name
2684 **/
2685 BOOLEAN
2686 EFIAPI
2687 IsValidCommandName(
2688 IN CONST CHAR16 *CommandName
2689 )
2690 {
2691 UINTN Count;
2692 if (CommandName == NULL) {
2693 ASSERT(FALSE);
2694 return (FALSE);
2695 }
2696 for ( Count = 0
2697 ; Count < sizeof(InvalidChars) / sizeof(InvalidChars[0])
2698 ; Count++
2699 ){
2700 if (ScanMem16(CommandName, StrSize(CommandName), InvalidChars[Count]) != NULL) {
2701 return (FALSE);
2702 }
2703 }
2704 return (TRUE);
2705 }
2706
2707 /**
2708 Function to process a NSH script file via SHELL_FILE_HANDLE.
2709
2710 @param[in] Handle The handle to the already opened file.
2711 @param[in] Name The name of the script file.
2712
2713 @retval EFI_SUCCESS the script completed successfully
2714 **/
2715 EFI_STATUS
2716 EFIAPI
2717 RunScriptFileHandle (
2718 IN SHELL_FILE_HANDLE Handle,
2719 IN CONST CHAR16 *Name
2720 )
2721 {
2722 EFI_STATUS Status;
2723 SCRIPT_FILE *NewScriptFile;
2724 UINTN LoopVar;
2725 UINTN PrintBuffSize;
2726 CHAR16 *CommandLine;
2727 CHAR16 *CommandLine2;
2728 CHAR16 *CommandLine3;
2729 SCRIPT_COMMAND_LIST *LastCommand;
2730 BOOLEAN Ascii;
2731 BOOLEAN PreScriptEchoState;
2732 BOOLEAN PreCommandEchoState;
2733 CONST CHAR16 *CurDir;
2734 UINTN LineCount;
2735 CHAR16 LeString[50];
2736 LIST_ENTRY OldBufferList;
2737
2738 ASSERT(!ShellCommandGetScriptExit());
2739
2740 PreScriptEchoState = ShellCommandGetEchoState();
2741 PrintBuffSize = PcdGet16(PcdShellPrintBufferSize);
2742
2743 NewScriptFile = (SCRIPT_FILE*)AllocateZeroPool(sizeof(SCRIPT_FILE));
2744 if (NewScriptFile == NULL) {
2745 return (EFI_OUT_OF_RESOURCES);
2746 }
2747
2748 //
2749 // Set up the name
2750 //
2751 ASSERT(NewScriptFile->ScriptName == NULL);
2752 NewScriptFile->ScriptName = StrnCatGrow(&NewScriptFile->ScriptName, NULL, Name, 0);
2753 if (NewScriptFile->ScriptName == NULL) {
2754 DeleteScriptFileStruct(NewScriptFile);
2755 return (EFI_OUT_OF_RESOURCES);
2756 }
2757
2758 //
2759 // Save the parameters (used to replace %0 to %9 later on)
2760 //
2761 NewScriptFile->Argc = ShellInfoObject.NewShellParametersProtocol->Argc;
2762 if (NewScriptFile->Argc != 0) {
2763 NewScriptFile->Argv = (CHAR16**)AllocateZeroPool(NewScriptFile->Argc * sizeof(CHAR16*));
2764 if (NewScriptFile->Argv == NULL) {
2765 DeleteScriptFileStruct(NewScriptFile);
2766 return (EFI_OUT_OF_RESOURCES);
2767 }
2768 for (LoopVar = 0 ; LoopVar < 10 && LoopVar < NewScriptFile->Argc; LoopVar++) {
2769 ASSERT(NewScriptFile->Argv[LoopVar] == NULL);
2770 NewScriptFile->Argv[LoopVar] = StrnCatGrow(&NewScriptFile->Argv[LoopVar], NULL, ShellInfoObject.NewShellParametersProtocol->Argv[LoopVar], 0);
2771 if (NewScriptFile->Argv[LoopVar] == NULL) {
2772 DeleteScriptFileStruct(NewScriptFile);
2773 return (EFI_OUT_OF_RESOURCES);
2774 }
2775 }
2776 } else {
2777 NewScriptFile->Argv = NULL;
2778 }
2779
2780 InitializeListHead(&NewScriptFile->CommandList);
2781 InitializeListHead(&NewScriptFile->SubstList);
2782
2783 //
2784 // Now build the list of all script commands.
2785 //
2786 LineCount = 0;
2787 while(!ShellFileHandleEof(Handle)) {
2788 CommandLine = ShellFileHandleReturnLine(Handle, &Ascii);
2789 LineCount++;
2790 if (CommandLine == NULL || StrLen(CommandLine) == 0 || CommandLine[0] == '#') {
2791 SHELL_FREE_NON_NULL(CommandLine);
2792 continue;
2793 }
2794 NewScriptFile->CurrentCommand = AllocateZeroPool(sizeof(SCRIPT_COMMAND_LIST));
2795 if (NewScriptFile->CurrentCommand == NULL) {
2796 SHELL_FREE_NON_NULL(CommandLine);
2797 DeleteScriptFileStruct(NewScriptFile);
2798 return (EFI_OUT_OF_RESOURCES);
2799 }
2800
2801 NewScriptFile->CurrentCommand->Cl = CommandLine;
2802 NewScriptFile->CurrentCommand->Data = NULL;
2803 NewScriptFile->CurrentCommand->Line = LineCount;
2804
2805 InsertTailList(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);
2806 }
2807
2808 //
2809 // Add this as the topmost script file
2810 //
2811 ShellCommandSetNewScript (NewScriptFile);
2812
2813 //
2814 // Now enumerate through the commands and run each one.
2815 //
2816 CommandLine = AllocateZeroPool(PrintBuffSize);
2817 if (CommandLine == NULL) {
2818 DeleteScriptFileStruct(NewScriptFile);
2819 return (EFI_OUT_OF_RESOURCES);
2820 }
2821 CommandLine2 = AllocateZeroPool(PrintBuffSize);
2822 if (CommandLine2 == NULL) {
2823 FreePool(CommandLine);
2824 DeleteScriptFileStruct(NewScriptFile);
2825 return (EFI_OUT_OF_RESOURCES);
2826 }
2827
2828 for ( NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetFirstNode(&NewScriptFile->CommandList)
2829 ; !IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)
2830 ; // conditional increment in the body of the loop
2831 ){
2832 ASSERT(CommandLine2 != NULL);
2833 StrnCpyS( CommandLine2,
2834 PrintBuffSize/sizeof(CHAR16),
2835 NewScriptFile->CurrentCommand->Cl,
2836 PrintBuffSize/sizeof(CHAR16) - 1
2837 );
2838
2839 SaveBufferList(&OldBufferList);
2840
2841 //
2842 // NULL out comments
2843 //
2844 for (CommandLine3 = CommandLine2 ; CommandLine3 != NULL && *CommandLine3 != CHAR_NULL ; CommandLine3++) {
2845 if (*CommandLine3 == L'^') {
2846 if ( *(CommandLine3+1) == L':') {
2847 CopyMem(CommandLine3, CommandLine3+1, StrSize(CommandLine3) - sizeof(CommandLine3[0]));
2848 } else if (*(CommandLine3+1) == L'#') {
2849 CommandLine3++;
2850 }
2851 } else if (*CommandLine3 == L'#') {
2852 *CommandLine3 = CHAR_NULL;
2853 }
2854 }
2855
2856 if (CommandLine2 != NULL && StrLen(CommandLine2) >= 1) {
2857 //
2858 // Due to variability in starting the find and replace action we need to have both buffers the same.
2859 //
2860 StrnCpyS( CommandLine,
2861 PrintBuffSize/sizeof(CHAR16),
2862 CommandLine2,
2863 PrintBuffSize/sizeof(CHAR16) - 1
2864 );
2865
2866 //
2867 // Remove the %0 to %9 from the command line (if we have some arguments)
2868 //
2869 if (NewScriptFile->Argv != NULL) {
2870 switch (NewScriptFile->Argc) {
2871 default:
2872 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PrintBuffSize, L"%9", NewScriptFile->Argv[9], FALSE, FALSE);
2873 ASSERT_EFI_ERROR(Status);
2874 case 9:
2875 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PrintBuffSize, L"%8", NewScriptFile->Argv[8], FALSE, FALSE);
2876 ASSERT_EFI_ERROR(Status);
2877 case 8:
2878 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PrintBuffSize, L"%7", NewScriptFile->Argv[7], FALSE, FALSE);
2879 ASSERT_EFI_ERROR(Status);
2880 case 7:
2881 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PrintBuffSize, L"%6", NewScriptFile->Argv[6], FALSE, FALSE);
2882 ASSERT_EFI_ERROR(Status);
2883 case 6:
2884 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PrintBuffSize, L"%5", NewScriptFile->Argv[5], FALSE, FALSE);
2885 ASSERT_EFI_ERROR(Status);
2886 case 5:
2887 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PrintBuffSize, L"%4", NewScriptFile->Argv[4], FALSE, FALSE);
2888 ASSERT_EFI_ERROR(Status);
2889 case 4:
2890 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PrintBuffSize, L"%3", NewScriptFile->Argv[3], FALSE, FALSE);
2891 ASSERT_EFI_ERROR(Status);
2892 case 3:
2893 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PrintBuffSize, L"%2", NewScriptFile->Argv[2], FALSE, FALSE);
2894 ASSERT_EFI_ERROR(Status);
2895 case 2:
2896 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PrintBuffSize, L"%1", NewScriptFile->Argv[1], FALSE, FALSE);
2897 ASSERT_EFI_ERROR(Status);
2898 case 1:
2899 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PrintBuffSize, L"%0", NewScriptFile->Argv[0], FALSE, FALSE);
2900 ASSERT_EFI_ERROR(Status);
2901 break;
2902 case 0:
2903 break;
2904 }
2905 }
2906 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PrintBuffSize, L"%1", L"\"\"", FALSE, FALSE);
2907 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PrintBuffSize, L"%2", L"\"\"", FALSE, FALSE);
2908 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PrintBuffSize, L"%3", L"\"\"", FALSE, FALSE);
2909 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PrintBuffSize, L"%4", L"\"\"", FALSE, FALSE);
2910 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PrintBuffSize, L"%5", L"\"\"", FALSE, FALSE);
2911 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PrintBuffSize, L"%6", L"\"\"", FALSE, FALSE);
2912 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PrintBuffSize, L"%7", L"\"\"", FALSE, FALSE);
2913 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PrintBuffSize, L"%8", L"\"\"", FALSE, FALSE);
2914 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PrintBuffSize, L"%9", L"\"\"", FALSE, FALSE);
2915
2916 StrnCpyS( CommandLine2,
2917 PrintBuffSize/sizeof(CHAR16),
2918 CommandLine,
2919 PrintBuffSize/sizeof(CHAR16) - 1
2920 );
2921
2922 LastCommand = NewScriptFile->CurrentCommand;
2923
2924 for (CommandLine3 = CommandLine2 ; CommandLine3[0] == L' ' ; CommandLine3++);
2925
2926 if (CommandLine3 != NULL && CommandLine3[0] == L':' ) {
2927 //
2928 // This line is a goto target / label
2929 //
2930 } else {
2931 if (CommandLine3 != NULL && StrLen(CommandLine3) > 0) {
2932 if (CommandLine3[0] == L'@') {
2933 //
2934 // We need to save the current echo state
2935 // and disable echo for just this command.
2936 //
2937 PreCommandEchoState = ShellCommandGetEchoState();
2938 ShellCommandSetEchoState(FALSE);
2939 Status = RunCommand(CommandLine3+1);
2940
2941 //
2942 // If command was "@echo -off" or "@echo -on" then don't restore echo state
2943 //
2944 if (StrCmp (L"@echo -off", CommandLine3) != 0 &&
2945 StrCmp (L"@echo -on", CommandLine3) != 0) {
2946 //
2947 // Now restore the pre-'@' echo state.
2948 //
2949 ShellCommandSetEchoState(PreCommandEchoState);
2950 }
2951 } else {
2952 if (ShellCommandGetEchoState()) {
2953 CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv(L"cwd");
2954 if (CurDir != NULL && StrLen(CurDir) > 1) {
2955 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CURDIR), ShellInfoObject.HiiHandle, CurDir);
2956 } else {
2957 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_SHELL), ShellInfoObject.HiiHandle);
2958 }
2959 ShellPrintEx(-1, -1, L"%s\r\n", CommandLine2);
2960 }
2961 Status = RunCommand(CommandLine3);
2962 }
2963 }
2964
2965 if (ShellCommandGetScriptExit()) {
2966 //
2967 // ShellCommandGetExitCode() always returns a UINT64
2968 //
2969 UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", ShellCommandGetExitCode());
2970 DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE););
2971 InternalEfiShellSetEnv(L"lasterror", LeString, TRUE);
2972
2973 ShellCommandRegisterExit(FALSE, 0);
2974 Status = EFI_SUCCESS;
2975 RestoreBufferList(&OldBufferList);
2976 break;
2977 }
2978 if (ShellGetExecutionBreakFlag()) {
2979 RestoreBufferList(&OldBufferList);
2980 break;
2981 }
2982 if (EFI_ERROR(Status)) {
2983 RestoreBufferList(&OldBufferList);
2984 break;
2985 }
2986 if (ShellCommandGetExit()) {
2987 RestoreBufferList(&OldBufferList);
2988 break;
2989 }
2990 }
2991 //
2992 // If that commend did not update the CurrentCommand then we need to advance it...
2993 //
2994 if (LastCommand == NewScriptFile->CurrentCommand) {
2995 NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetNextNode(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);
2996 if (!IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)) {
2997 NewScriptFile->CurrentCommand->Reset = TRUE;
2998 }
2999 }
3000 } else {
3001 NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetNextNode(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);
3002 if (!IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)) {
3003 NewScriptFile->CurrentCommand->Reset = TRUE;
3004 }
3005 }
3006 RestoreBufferList(&OldBufferList);
3007 }
3008
3009
3010 FreePool(CommandLine);
3011 FreePool(CommandLine2);
3012 ShellCommandSetNewScript (NULL);
3013
3014 //
3015 // Only if this was the last script reset the state.
3016 //
3017 if (ShellCommandGetCurrentScriptFile()==NULL) {
3018 ShellCommandSetEchoState(PreScriptEchoState);
3019 }
3020 return (EFI_SUCCESS);
3021 }
3022
3023 /**
3024 Function to process a NSH script file.
3025
3026 @param[in] ScriptPath Pointer to the script file name (including file system path).
3027 @param[in] Handle the handle of the script file already opened.
3028 @param[in] CmdLine the command line to run.
3029 @param[in] ParamProtocol the shell parameters protocol pointer
3030
3031 @retval EFI_SUCCESS the script completed successfully
3032 **/
3033 EFI_STATUS
3034 EFIAPI
3035 RunScriptFile (
3036 IN CONST CHAR16 *ScriptPath,
3037 IN SHELL_FILE_HANDLE Handle OPTIONAL,
3038 IN CONST CHAR16 *CmdLine,
3039 IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol
3040 )
3041 {
3042 EFI_STATUS Status;
3043 SHELL_FILE_HANDLE FileHandle;
3044 UINTN Argc;
3045 CHAR16 **Argv;
3046
3047 if (ShellIsFile(ScriptPath) != EFI_SUCCESS) {
3048 return (EFI_INVALID_PARAMETER);
3049 }
3050
3051 //
3052 // get the argc and argv updated for scripts
3053 //
3054 Status = UpdateArgcArgv(ParamProtocol, CmdLine, Script_File_Name, &Argv, &Argc);
3055 if (!EFI_ERROR(Status)) {
3056
3057 if (Handle == NULL) {
3058 //
3059 // open the file
3060 //
3061 Status = ShellOpenFileByName(ScriptPath, &FileHandle, EFI_FILE_MODE_READ, 0);
3062 if (!EFI_ERROR(Status)) {
3063 //
3064 // run it
3065 //
3066 Status = RunScriptFileHandle(FileHandle, ScriptPath);
3067
3068 //
3069 // now close the file
3070 //
3071 ShellCloseFile(&FileHandle);
3072 }
3073 } else {
3074 Status = RunScriptFileHandle(Handle, ScriptPath);
3075 }
3076 }
3077
3078 //
3079 // This is guaranteed to be called after UpdateArgcArgv no matter what else happened.
3080 // This is safe even if the update API failed. In this case, it may be a no-op.
3081 //
3082 RestoreArgcArgv(ParamProtocol, &Argv, &Argc);
3083
3084 return (Status);
3085 }
3086
3087 /**
3088 Return the pointer to the first occurrence of any character from a list of characters.
3089
3090 @param[in] String the string to parse
3091 @param[in] CharacterList the list of character to look for
3092 @param[in] EscapeCharacter An escape character to skip
3093
3094 @return the location of the first character in the string
3095 @retval CHAR_NULL no instance of any character in CharacterList was found in String
3096 **/
3097 CONST CHAR16*
3098 EFIAPI
3099 FindFirstCharacter(
3100 IN CONST CHAR16 *String,
3101 IN CONST CHAR16 *CharacterList,
3102 IN CONST CHAR16 EscapeCharacter
3103 )
3104 {
3105 UINT32 WalkChar;
3106 UINT32 WalkStr;
3107
3108 for (WalkStr = 0; WalkStr < StrLen(String); WalkStr++) {
3109 if (String[WalkStr] == EscapeCharacter) {
3110 WalkStr++;
3111 continue;
3112 }
3113 for (WalkChar = 0; WalkChar < StrLen(CharacterList); WalkChar++) {
3114 if (String[WalkStr] == CharacterList[WalkChar]) {
3115 return (&String[WalkStr]);
3116 }
3117 }
3118 }
3119 return (String + StrLen(String));
3120 }