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