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