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