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