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