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