]> git.proxmox.com Git - mirror_edk2.git/blob - ShellPkg/Application/Shell/Shell.c
ShellPkg: Fix changing to file system with 2 colons like "fs0::"
[mirror_edk2.git] / ShellPkg / Application / Shell / Shell.c
1 /** @file
2 This is THE shell (application)
3
4 Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
5 Copyright (c) 2013, Hewlett-Packard Development Company, L.P.
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
10
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13
14 **/
15
16 #include "Shell.h"
17
18 //
19 // Initialize the global structure
20 //
21 SHELL_INFO ShellInfoObject = {
22 NULL,
23 NULL,
24 FALSE,
25 FALSE,
26 {
27 {{
28 0,
29 0,
30 0,
31 0,
32 0,
33 0,
34 0,
35 0,
36 0
37 }},
38 0,
39 NULL,
40 NULL
41 },
42 {{NULL, NULL}, NULL},
43 {
44 {{NULL, NULL}, NULL},
45 0,
46 0,
47 TRUE
48 },
49 NULL,
50 0,
51 NULL,
52 NULL,
53 NULL,
54 NULL,
55 NULL,
56 {{NULL, NULL}, NULL, NULL},
57 {{NULL, NULL}, NULL, NULL},
58 NULL,
59 NULL,
60 NULL,
61 NULL,
62 NULL,
63 NULL,
64 NULL,
65 NULL,
66 FALSE
67 };
68
69 STATIC CONST CHAR16 mScriptExtension[] = L".NSH";
70 STATIC CONST CHAR16 mExecutableExtensions[] = L".NSH;.EFI";
71 STATIC CONST CHAR16 mStartupScript[] = L"startup.nsh";
72
73 /**
74 Cleans off leading and trailing spaces and tabs.
75
76 @param[in] String pointer to the string to trim them off.
77 **/
78 EFI_STATUS
79 EFIAPI
80 TrimSpaces(
81 IN CHAR16 **String
82 )
83 {
84 ASSERT(String != NULL);
85 ASSERT(*String!= NULL);
86 //
87 // Remove any spaces and tabs at the beginning of the (*String).
88 //
89 while (((*String)[0] == L' ') || ((*String)[0] == L'\t')) {
90 CopyMem((*String), (*String)+1, StrSize((*String)) - sizeof((*String)[0]));
91 }
92
93 //
94 // Remove any spaces and tabs at the end of the (*String).
95 //
96 while ((StrLen (*String) > 0) && (((*String)[StrLen((*String))-1] == L' ') || ((*String)[StrLen((*String))-1] == L'\t'))) {
97 (*String)[StrLen((*String))-1] = CHAR_NULL;
98 }
99
100 return (EFI_SUCCESS);
101 }
102
103 /**
104 Find a command line contains a split operation
105
106 @param[in] CmdLine The command line to parse.
107
108 @retval A pointer to the | character in CmdLine or NULL if not present.
109 **/
110 CONST CHAR16*
111 EFIAPI
112 FindSplit(
113 IN CONST CHAR16 *CmdLine
114 )
115 {
116 CONST CHAR16 *TempSpot;
117 TempSpot = NULL;
118 if (StrStr(CmdLine, L"|") != NULL) {
119 for (TempSpot = CmdLine ; TempSpot != NULL && *TempSpot != CHAR_NULL ; TempSpot++) {
120 if (*TempSpot == L'^' && *(TempSpot+1) == L'|') {
121 TempSpot++;
122 } else if (*TempSpot == L'|') {
123 break;
124 }
125 }
126 }
127 return (TempSpot);
128 }
129
130 /**
131 Determine if a command line contains a split operation
132
133 @param[in] CmdLine The command line to parse.
134
135 @retval TRUE CmdLine has a valid split.
136 @retval FALSE CmdLine does not have a valid split.
137 **/
138 BOOLEAN
139 EFIAPI
140 ContainsSplit(
141 IN CONST CHAR16 *CmdLine
142 )
143 {
144 CONST CHAR16 *TempSpot;
145 TempSpot = FindSplit(CmdLine);
146 return (BOOLEAN) ((TempSpot != NULL) && (*TempSpot != CHAR_NULL));
147 }
148
149 /**
150 Function to start monitoring for CTRL-S using SimpleTextInputEx. This
151 feature's enabled state was not known when the shell initially launched.
152
153 @retval EFI_SUCCESS The feature is enabled.
154 @retval EFI_OUT_OF_RESOURCES There is not enough mnemory available.
155 **/
156 EFI_STATUS
157 EFIAPI
158 InternalEfiShellStartCtrlSMonitor(
159 VOID
160 )
161 {
162 EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleEx;
163 EFI_KEY_DATA KeyData;
164 EFI_STATUS Status;
165
166 Status = gBS->OpenProtocol(
167 gST->ConsoleInHandle,
168 &gEfiSimpleTextInputExProtocolGuid,
169 (VOID**)&SimpleEx,
170 gImageHandle,
171 NULL,
172 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
173 if (EFI_ERROR(Status)) {
174 ShellPrintHiiEx(
175 -1,
176 -1,
177 NULL,
178 STRING_TOKEN (STR_SHELL_NO_IN_EX),
179 ShellInfoObject.HiiHandle);
180 return (EFI_SUCCESS);
181 }
182
183 KeyData.KeyState.KeyToggleState = 0;
184 KeyData.Key.ScanCode = 0;
185 KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;
186 KeyData.Key.UnicodeChar = L's';
187
188 Status = SimpleEx->RegisterKeyNotify(
189 SimpleEx,
190 &KeyData,
191 NotificationFunction,
192 &ShellInfoObject.CtrlSNotifyHandle1);
193
194 KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;
195 if (!EFI_ERROR(Status)) {
196 Status = SimpleEx->RegisterKeyNotify(
197 SimpleEx,
198 &KeyData,
199 NotificationFunction,
200 &ShellInfoObject.CtrlSNotifyHandle2);
201 }
202 KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;
203 KeyData.Key.UnicodeChar = 19;
204
205 if (!EFI_ERROR(Status)) {
206 Status = SimpleEx->RegisterKeyNotify(
207 SimpleEx,
208 &KeyData,
209 NotificationFunction,
210 &ShellInfoObject.CtrlSNotifyHandle3);
211 }
212 KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;
213 if (!EFI_ERROR(Status)) {
214 Status = SimpleEx->RegisterKeyNotify(
215 SimpleEx,
216 &KeyData,
217 NotificationFunction,
218 &ShellInfoObject.CtrlSNotifyHandle4);
219 }
220 return (Status);
221 }
222
223
224
225 /**
226 The entry point for the application.
227
228 @param[in] ImageHandle The firmware allocated handle for the EFI image.
229 @param[in] SystemTable A pointer to the EFI System Table.
230
231 @retval EFI_SUCCESS The entry point is executed successfully.
232 @retval other Some error occurs when executing this entry point.
233
234 **/
235 EFI_STATUS
236 EFIAPI
237 UefiMain (
238 IN EFI_HANDLE ImageHandle,
239 IN EFI_SYSTEM_TABLE *SystemTable
240 )
241 {
242 EFI_STATUS Status;
243 CHAR16 *TempString;
244 UINTN Size;
245 EFI_HANDLE ConInHandle;
246 EFI_SIMPLE_TEXT_INPUT_PROTOCOL *OldConIn;
247 UINTN ExitDataSize;
248 CHAR16 *ExitData;
249 SHELL_STATUS ExitStatus;
250
251 if (PcdGet8(PcdShellSupportLevel) > 3) {
252 return (EFI_UNSUPPORTED);
253 }
254
255 //
256 // Clear the screen
257 //
258 Status = gST->ConOut->ClearScreen(gST->ConOut);
259 if (EFI_ERROR(Status)) {
260 return (Status);
261 }
262
263 //
264 // Populate the global structure from PCDs
265 //
266 ShellInfoObject.ImageDevPath = NULL;
267 ShellInfoObject.FileDevPath = NULL;
268 ShellInfoObject.PageBreakEnabled = PcdGetBool(PcdShellPageBreakDefault);
269 ShellInfoObject.ViewingSettings.InsertMode = PcdGetBool(PcdShellInsertModeDefault);
270 ShellInfoObject.LogScreenCount = PcdGet8 (PcdShellScreenLogCount );
271
272 //
273 // verify we dont allow for spec violation
274 //
275 ASSERT(ShellInfoObject.LogScreenCount >= 3);
276
277 //
278 // Initialize the LIST ENTRY objects...
279 //
280 InitializeListHead(&ShellInfoObject.BufferToFreeList.Link);
281 InitializeListHead(&ShellInfoObject.ViewingSettings.CommandHistory.Link);
282 InitializeListHead(&ShellInfoObject.SplitList.Link);
283
284 //
285 // Check PCDs for optional features that are not implemented yet.
286 //
287 if ( PcdGetBool(PcdShellSupportOldProtocols)
288 || !FeaturePcdGet(PcdShellRequireHiiPlatform)
289 || FeaturePcdGet(PcdShellSupportFrameworkHii)
290 ) {
291 return (EFI_UNSUPPORTED);
292 }
293
294 //
295 // turn off the watchdog timer
296 //
297 gBS->SetWatchdogTimer (0, 0, 0, NULL);
298
299 //
300 // install our console logger. This will keep a log of the output for back-browsing
301 //
302 Status = ConsoleLoggerInstall(ShellInfoObject.LogScreenCount, &ShellInfoObject.ConsoleInfo);
303 if(EFI_ERROR (Status)) {
304 ExitStatus = (SHELL_STATUS) (Status & (~MAX_BIT));
305 } else {
306 ExitStatus = SHELL_SUCCESS;
307 }
308
309 if (!EFI_ERROR(Status)) {
310 //
311 // Enable the cursor to be visible
312 //
313 gST->ConOut->EnableCursor (gST->ConOut, TRUE);
314
315 //
316 // If supporting EFI 1.1 we need to install HII protocol
317 // only do this if PcdShellRequireHiiPlatform == FALSE
318 //
319 // remove EFI_UNSUPPORTED check above when complete.
320 ///@todo add support for Framework HII
321
322 //
323 // install our (solitary) HII package
324 //
325 ShellInfoObject.HiiHandle = HiiAddPackages (&gEfiCallerIdGuid, gImageHandle, ShellStrings, NULL);
326 if (ShellInfoObject.HiiHandle == NULL) {
327 if (PcdGetBool(PcdShellSupportFrameworkHii)) {
328 ///@todo Add our package into Framework HII
329 }
330 if (ShellInfoObject.HiiHandle == NULL) {
331 Status = EFI_NOT_STARTED;
332 goto FreeResources;
333 }
334 }
335
336 //
337 // create and install the EfiShellParametersProtocol
338 //
339 Status = CreatePopulateInstallShellParametersProtocol(&ShellInfoObject.NewShellParametersProtocol, &ShellInfoObject.RootShellInstance);
340 ASSERT_EFI_ERROR(Status);
341 ASSERT(ShellInfoObject.NewShellParametersProtocol != NULL);
342
343 //
344 // create and install the EfiShellProtocol
345 //
346 Status = CreatePopulateInstallShellProtocol(&ShellInfoObject.NewEfiShellProtocol);
347 ASSERT_EFI_ERROR(Status);
348 ASSERT(ShellInfoObject.NewEfiShellProtocol != NULL);
349
350 //
351 // Now initialize the shell library (it requires Shell Parameters protocol)
352 //
353 Status = ShellInitialize();
354 ASSERT_EFI_ERROR(Status);
355
356 Status = CommandInit();
357 ASSERT_EFI_ERROR(Status);
358
359 //
360 // Check the command line
361 //
362 Status = ProcessCommandLine ();
363 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 Function allocates a new command line and replaces all instances of environment
1281 variable names that are correctly preset to their values.
1282
1283 If the return value is not NULL the memory must be caller freed.
1284
1285 @param[in] OriginalCommandLine The original command line
1286
1287 @retval NULL An error ocurred.
1288 @return The new command line with no environment variables present.
1289 **/
1290 CHAR16*
1291 EFIAPI
1292 ShellConvertVariables (
1293 IN CONST CHAR16 *OriginalCommandLine
1294 )
1295 {
1296 CONST CHAR16 *MasterEnvList;
1297 UINTN NewSize;
1298 CHAR16 *NewCommandLine1;
1299 CHAR16 *NewCommandLine2;
1300 CHAR16 *Temp;
1301 CHAR16 *Temp2;
1302 UINTN ItemSize;
1303 CHAR16 *ItemTemp;
1304 SCRIPT_FILE *CurrentScriptFile;
1305 ALIAS_LIST *AliasListNode;
1306
1307 ASSERT(OriginalCommandLine != NULL);
1308
1309 ItemSize = 0;
1310 NewSize = StrSize(OriginalCommandLine);
1311 CurrentScriptFile = ShellCommandGetCurrentScriptFile();
1312 Temp = NULL;
1313
1314 ///@todo update this to handle the %0 - %9 for scripting only (borrow from line 1256 area) ? ? ?
1315
1316 //
1317 // calculate the size required for the post-conversion string...
1318 //
1319 if (CurrentScriptFile != NULL) {
1320 for (AliasListNode = (ALIAS_LIST*)GetFirstNode(&CurrentScriptFile->SubstList)
1321 ; !IsNull(&CurrentScriptFile->SubstList, &AliasListNode->Link)
1322 ; AliasListNode = (ALIAS_LIST*)GetNextNode(&CurrentScriptFile->SubstList, &AliasListNode->Link)
1323 ){
1324 for (Temp = StrStr(OriginalCommandLine, AliasListNode->Alias)
1325 ; Temp != NULL
1326 ; Temp = StrStr(Temp+1, AliasListNode->Alias)
1327 ){
1328 //
1329 // we need a preceeding and if there is space no ^ preceeding (if no space ignore)
1330 //
1331 if ((((Temp-OriginalCommandLine)>2) && *(Temp-2) != L'^') || ((Temp-OriginalCommandLine)<=2)) {
1332 NewSize += StrSize(AliasListNode->CommandString);
1333 }
1334 }
1335 }
1336 }
1337
1338 for (MasterEnvList = EfiShellGetEnv(NULL)
1339 ; MasterEnvList != NULL && *MasterEnvList != CHAR_NULL //&& *(MasterEnvList+1) != CHAR_NULL
1340 ; MasterEnvList += StrLen(MasterEnvList) + 1
1341 ){
1342 if (StrSize(MasterEnvList) > ItemSize) {
1343 ItemSize = StrSize(MasterEnvList);
1344 }
1345 for (Temp = StrStr(OriginalCommandLine, MasterEnvList)
1346 ; Temp != NULL
1347 ; Temp = StrStr(Temp+1, MasterEnvList)
1348 ){
1349 //
1350 // we need a preceeding and following % and if there is space no ^ preceeding (if no space ignore)
1351 //
1352 if (*(Temp-1) == L'%' && *(Temp+StrLen(MasterEnvList)) == L'%' &&
1353 ((((Temp-OriginalCommandLine)>2) && *(Temp-2) != L'^') || ((Temp-OriginalCommandLine)<=2))) {
1354 NewSize+=StrSize(EfiShellGetEnv(MasterEnvList));
1355 }
1356 }
1357 }
1358
1359 //
1360 // now do the replacements...
1361 //
1362 NewCommandLine1 = AllocateZeroPool(NewSize);
1363 NewCommandLine2 = AllocateZeroPool(NewSize);
1364 ItemTemp = AllocateZeroPool(ItemSize+(2*sizeof(CHAR16)));
1365 if (NewCommandLine1 == NULL || NewCommandLine2 == NULL || ItemTemp == NULL) {
1366 SHELL_FREE_NON_NULL(NewCommandLine1);
1367 SHELL_FREE_NON_NULL(NewCommandLine2);
1368 SHELL_FREE_NON_NULL(ItemTemp);
1369 return (NULL);
1370 }
1371 StrCpy(NewCommandLine1, OriginalCommandLine);
1372 for (MasterEnvList = EfiShellGetEnv(NULL)
1373 ; MasterEnvList != NULL && *MasterEnvList != CHAR_NULL //&& *(MasterEnvList+1) != CHAR_NULL
1374 ; MasterEnvList += StrLen(MasterEnvList) + 1
1375 ){
1376 StrCpy(ItemTemp, L"%");
1377 StrCat(ItemTemp, MasterEnvList);
1378 StrCat(ItemTemp, L"%");
1379 ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, ItemTemp, EfiShellGetEnv(MasterEnvList), TRUE, FALSE);
1380 StrCpy(NewCommandLine1, NewCommandLine2);
1381 }
1382 if (CurrentScriptFile != NULL) {
1383 for (AliasListNode = (ALIAS_LIST*)GetFirstNode(&CurrentScriptFile->SubstList)
1384 ; !IsNull(&CurrentScriptFile->SubstList, &AliasListNode->Link)
1385 ; AliasListNode = (ALIAS_LIST*)GetNextNode(&CurrentScriptFile->SubstList, &AliasListNode->Link)
1386 ){
1387 ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, AliasListNode->Alias, AliasListNode->CommandString, TRUE, FALSE);
1388 StrCpy(NewCommandLine1, NewCommandLine2);
1389 }
1390
1391 //
1392 // Remove non-existant environment variables in scripts only
1393 //
1394 for (Temp = NewCommandLine1 ; Temp != NULL ; ) {
1395 Temp = StrStr(Temp, L"%");
1396 if (Temp == NULL) {
1397 break;
1398 }
1399 while (*(Temp - 1) == L'^') {
1400 Temp = StrStr(Temp + 1, L"%");
1401 if (Temp == NULL) {
1402 break;
1403 }
1404 }
1405 if (Temp == NULL) {
1406 break;
1407 }
1408
1409 Temp2 = StrStr(Temp + 1, L"%");
1410 if (Temp2 == NULL) {
1411 break;
1412 }
1413 while (*(Temp2 - 1) == L'^') {
1414 Temp2 = StrStr(Temp2 + 1, L"%");
1415 if (Temp2 == NULL) {
1416 break;
1417 }
1418 }
1419 if (Temp2 == NULL) {
1420 break;
1421 }
1422
1423 Temp2++;
1424 CopyMem(Temp, Temp2, StrSize(Temp2));
1425 }
1426
1427 }
1428
1429 //
1430 // Now cleanup any straggler intentionally ignored "%" characters
1431 //
1432 ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, L"^%", L"%", TRUE, FALSE);
1433 StrCpy(NewCommandLine1, NewCommandLine2);
1434
1435 FreePool(NewCommandLine2);
1436 FreePool(ItemTemp);
1437
1438 return (NewCommandLine1);
1439 }
1440
1441 /**
1442 Internal function to run a command line with pipe usage.
1443
1444 @param[in] CmdLine The pointer to the command line.
1445 @param[in] StdIn The pointer to the Standard input.
1446 @param[in] StdOut The pointer to the Standard output.
1447
1448 @param[out] ExitStatus The exit code of the last command in the pipeline.
1449 Ignored if NULL.
1450
1451 @retval EFI_SUCCESS The split command is executed successfully.
1452 @retval other Some error occurs when executing the split command.
1453 **/
1454 EFI_STATUS
1455 EFIAPI
1456 RunSplitCommand(
1457 IN CONST CHAR16 *CmdLine,
1458 IN SHELL_FILE_HANDLE *StdIn,
1459 IN SHELL_FILE_HANDLE *StdOut,
1460 OUT SHELL_STATUS *ExitStatus
1461 )
1462 {
1463 EFI_STATUS Status;
1464 CHAR16 *NextCommandLine;
1465 CHAR16 *OurCommandLine;
1466 UINTN Size1;
1467 UINTN Size2;
1468 SPLIT_LIST *Split;
1469 SHELL_FILE_HANDLE *TempFileHandle;
1470 BOOLEAN Unicode;
1471
1472 ASSERT(StdOut == NULL);
1473
1474 ASSERT(StrStr(CmdLine, L"|") != NULL);
1475
1476 Status = EFI_SUCCESS;
1477 NextCommandLine = NULL;
1478 OurCommandLine = NULL;
1479 Size1 = 0;
1480 Size2 = 0;
1481
1482 NextCommandLine = StrnCatGrow(&NextCommandLine, &Size1, StrStr(CmdLine, L"|")+1, 0);
1483 OurCommandLine = StrnCatGrow(&OurCommandLine , &Size2, CmdLine , StrStr(CmdLine, L"|") - CmdLine);
1484
1485 if (NextCommandLine == NULL || OurCommandLine == NULL) {
1486 SHELL_FREE_NON_NULL(OurCommandLine);
1487 SHELL_FREE_NON_NULL(NextCommandLine);
1488 return (EFI_OUT_OF_RESOURCES);
1489 } else if (StrStr(OurCommandLine, L"|") != NULL || Size1 == 0 || Size2 == 0) {
1490 SHELL_FREE_NON_NULL(OurCommandLine);
1491 SHELL_FREE_NON_NULL(NextCommandLine);
1492 return (EFI_INVALID_PARAMETER);
1493 } else if (NextCommandLine[0] != CHAR_NULL &&
1494 NextCommandLine[0] == L'a' &&
1495 NextCommandLine[1] == L' '
1496 ){
1497 CopyMem(NextCommandLine, NextCommandLine+1, StrSize(NextCommandLine) - sizeof(NextCommandLine[0]));
1498 Unicode = FALSE;
1499 } else {
1500 Unicode = TRUE;
1501 }
1502
1503
1504 //
1505 // make a SPLIT_LIST item and add to list
1506 //
1507 Split = AllocateZeroPool(sizeof(SPLIT_LIST));
1508 ASSERT(Split != NULL);
1509 Split->SplitStdIn = StdIn;
1510 Split->SplitStdOut = ConvertEfiFileProtocolToShellHandle(CreateFileInterfaceMem(Unicode), NULL);
1511 ASSERT(Split->SplitStdOut != NULL);
1512 InsertHeadList(&ShellInfoObject.SplitList.Link, &Split->Link);
1513
1514 Status = RunCommand(OurCommandLine, NULL);
1515
1516 //
1517 // move the output from the first to the in to the second.
1518 //
1519 TempFileHandle = Split->SplitStdOut;
1520 if (Split->SplitStdIn == StdIn) {
1521 Split->SplitStdOut = NULL;
1522 } else {
1523 Split->SplitStdOut = Split->SplitStdIn;
1524 }
1525 Split->SplitStdIn = TempFileHandle;
1526 ShellInfoObject.NewEfiShellProtocol->SetFilePosition(ConvertShellHandleToEfiFileProtocol(Split->SplitStdIn), 0);
1527
1528 if (!EFI_ERROR(Status)) {
1529 Status = RunCommand(NextCommandLine, ExitStatus);
1530 }
1531
1532 //
1533 // remove the top level from the ScriptList
1534 //
1535 ASSERT((SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link) == Split);
1536 RemoveEntryList(&Split->Link);
1537
1538 //
1539 // Note that the original StdIn is now the StdOut...
1540 //
1541 if (Split->SplitStdOut != NULL && Split->SplitStdOut != StdIn) {
1542 ShellInfoObject.NewEfiShellProtocol->CloseFile(ConvertShellHandleToEfiFileProtocol(Split->SplitStdOut));
1543 }
1544 if (Split->SplitStdIn != NULL) {
1545 ShellInfoObject.NewEfiShellProtocol->CloseFile(ConvertShellHandleToEfiFileProtocol(Split->SplitStdIn));
1546 }
1547
1548 FreePool(Split);
1549 FreePool(NextCommandLine);
1550 FreePool(OurCommandLine);
1551
1552 return (Status);
1553 }
1554
1555 /**
1556 Take the original command line, substitute any variables, free
1557 the original string, return the modified copy.
1558
1559 @param[in] CmdLine pointer to the command line to update.
1560
1561 @retval EFI_SUCCESS the function was successful.
1562 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
1563 **/
1564 EFI_STATUS
1565 EFIAPI
1566 ShellSubstituteVariables(
1567 IN CHAR16 **CmdLine
1568 )
1569 {
1570 CHAR16 *NewCmdLine;
1571 NewCmdLine = ShellConvertVariables(*CmdLine);
1572 SHELL_FREE_NON_NULL(*CmdLine);
1573 if (NewCmdLine == NULL) {
1574 return (EFI_OUT_OF_RESOURCES);
1575 }
1576 *CmdLine = NewCmdLine;
1577 return (EFI_SUCCESS);
1578 }
1579
1580 /**
1581 Take the original command line, substitute any alias in the first group of space delimited characters, free
1582 the original string, return the modified copy.
1583
1584 @param[in] CmdLine pointer to the command line to update.
1585
1586 @retval EFI_SUCCESS the function was successful.
1587 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
1588 **/
1589 EFI_STATUS
1590 EFIAPI
1591 ShellSubstituteAliases(
1592 IN CHAR16 **CmdLine
1593 )
1594 {
1595 CHAR16 *NewCmdLine;
1596 CHAR16 *CommandName;
1597 EFI_STATUS Status;
1598 UINTN PostAliasSize;
1599 ASSERT(CmdLine != NULL);
1600 ASSERT(*CmdLine!= NULL);
1601
1602
1603 CommandName = NULL;
1604 if (StrStr((*CmdLine), L" ") == NULL){
1605 StrnCatGrow(&CommandName, NULL, (*CmdLine), 0);
1606 } else {
1607 StrnCatGrow(&CommandName, NULL, (*CmdLine), StrStr((*CmdLine), L" ") - (*CmdLine));
1608 }
1609
1610 //
1611 // This cannot happen 'inline' since the CmdLine can need extra space.
1612 //
1613 NewCmdLine = NULL;
1614 if (!ShellCommandIsCommandOnList(CommandName)) {
1615 //
1616 // Convert via alias
1617 //
1618 Status = ShellConvertAlias(&CommandName);
1619 if (EFI_ERROR(Status)){
1620 return (Status);
1621 }
1622 PostAliasSize = 0;
1623 NewCmdLine = StrnCatGrow(&NewCmdLine, &PostAliasSize, CommandName, 0);
1624 if (NewCmdLine == NULL) {
1625 SHELL_FREE_NON_NULL(CommandName);
1626 SHELL_FREE_NON_NULL(*CmdLine);
1627 return (EFI_OUT_OF_RESOURCES);
1628 }
1629 NewCmdLine = StrnCatGrow(&NewCmdLine, &PostAliasSize, StrStr((*CmdLine), L" "), 0);
1630 if (NewCmdLine == NULL) {
1631 SHELL_FREE_NON_NULL(CommandName);
1632 SHELL_FREE_NON_NULL(*CmdLine);
1633 return (EFI_OUT_OF_RESOURCES);
1634 }
1635 } else {
1636 NewCmdLine = StrnCatGrow(&NewCmdLine, NULL, (*CmdLine), 0);
1637 }
1638
1639 SHELL_FREE_NON_NULL(*CmdLine);
1640 SHELL_FREE_NON_NULL(CommandName);
1641
1642 //
1643 // re-assign the passed in double pointer to point to our newly allocated buffer
1644 //
1645 *CmdLine = NewCmdLine;
1646
1647 return (EFI_SUCCESS);
1648 }
1649
1650 /**
1651 Takes the Argv[0] part of the command line and determine the meaning of it.
1652
1653 @param[in] CmdName pointer to the command line to update.
1654
1655 @retval Internal_Command The name is an internal command.
1656 @retval File_Sys_Change the name is a file system change.
1657 @retval Script_File_Name the name is a NSH script file.
1658 @retval Unknown_Invalid the name is unknown.
1659 @retval Efi_Application the name is an application (.EFI).
1660 **/
1661 SHELL_OPERATION_TYPES
1662 EFIAPI
1663 GetOperationType(
1664 IN CONST CHAR16 *CmdName
1665 )
1666 {
1667 CHAR16* FileWithPath;
1668 CONST CHAR16* TempLocation;
1669 CONST CHAR16* TempLocation2;
1670
1671 FileWithPath = NULL;
1672 //
1673 // test for an internal command.
1674 //
1675 if (ShellCommandIsCommandOnList(CmdName)) {
1676 return (Internal_Command);
1677 }
1678
1679 //
1680 // Test for file system change request. anything ending with first : and cant have spaces.
1681 //
1682 if (CmdName[(StrLen(CmdName)-1)] == L':') {
1683 if ( StrStr(CmdName, L" ") != NULL
1684 || StrLen(StrStr(CmdName, L":")) > 1
1685 ) {
1686 return (Unknown_Invalid);
1687 }
1688 return (File_Sys_Change);
1689 }
1690
1691 //
1692 // Test for a file
1693 //
1694 if ((FileWithPath = ShellFindFilePathEx(CmdName, mExecutableExtensions)) != NULL) {
1695 //
1696 // See if that file has a script file extension
1697 //
1698 if (StrLen(FileWithPath) > 4) {
1699 TempLocation = FileWithPath+StrLen(FileWithPath)-4;
1700 TempLocation2 = mScriptExtension;
1701 if (StringNoCaseCompare((VOID*)(&TempLocation), (VOID*)(&TempLocation2)) == 0) {
1702 SHELL_FREE_NON_NULL(FileWithPath);
1703 return (Script_File_Name);
1704 }
1705 }
1706
1707 //
1708 // Was a file, but not a script. we treat this as an application.
1709 //
1710 SHELL_FREE_NON_NULL(FileWithPath);
1711 return (Efi_Application);
1712 }
1713
1714 SHELL_FREE_NON_NULL(FileWithPath);
1715 //
1716 // No clue what this is... return invalid flag...
1717 //
1718 return (Unknown_Invalid);
1719 }
1720
1721 /**
1722 Determine if the first item in a command line is valid.
1723
1724 @param[in] CmdLine The command line to parse.
1725
1726 @retval EFI_SUCCESS The item is valid.
1727 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
1728 @retval EFI_NOT_FOUND The operation type is unknown or invalid.
1729 **/
1730 EFI_STATUS
1731 EFIAPI
1732 IsValidSplit(
1733 IN CONST CHAR16 *CmdLine
1734 )
1735 {
1736 CHAR16 *Temp;
1737 CHAR16 *FirstParameter;
1738 CHAR16 *TempWalker;
1739 EFI_STATUS Status;
1740
1741 Temp = NULL;
1742
1743 Temp = StrnCatGrow(&Temp, NULL, CmdLine, 0);
1744 if (Temp == NULL) {
1745 return (EFI_OUT_OF_RESOURCES);
1746 }
1747
1748 FirstParameter = StrStr(Temp, L"|");
1749 if (FirstParameter != NULL) {
1750 *FirstParameter = CHAR_NULL;
1751 }
1752
1753 FirstParameter = NULL;
1754
1755 //
1756 // Process the command line
1757 //
1758 Status = ProcessCommandLineToFinal(&Temp);
1759
1760 if (!EFI_ERROR(Status)) {
1761 FirstParameter = AllocateZeroPool(StrSize(CmdLine));
1762 if (FirstParameter == NULL) {
1763 SHELL_FREE_NON_NULL(Temp);
1764 return (EFI_OUT_OF_RESOURCES);
1765 }
1766 TempWalker = (CHAR16*)Temp;
1767 GetNextParameter(&TempWalker, &FirstParameter);
1768
1769 if (GetOperationType(FirstParameter) == Unknown_Invalid) {
1770 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);
1771 SetLastError(SHELL_NOT_FOUND);
1772 Status = EFI_NOT_FOUND;
1773 }
1774 }
1775
1776 SHELL_FREE_NON_NULL(Temp);
1777 SHELL_FREE_NON_NULL(FirstParameter);
1778 return Status;
1779 }
1780
1781 /**
1782 Determine if a command line contains with a split contains only valid commands.
1783
1784 @param[in] CmdLine The command line to parse.
1785
1786 @retval EFI_SUCCESS CmdLine has only valid commands, application, or has no split.
1787 @retval EFI_ABORTED CmdLine has at least one invalid command or application.
1788 **/
1789 EFI_STATUS
1790 EFIAPI
1791 VerifySplit(
1792 IN CONST CHAR16 *CmdLine
1793 )
1794 {
1795 CONST CHAR16 *TempSpot;
1796 EFI_STATUS Status;
1797
1798 //
1799 // Verify up to the pipe or end character
1800 //
1801 Status = IsValidSplit(CmdLine);
1802 if (EFI_ERROR(Status)) {
1803 return (Status);
1804 }
1805
1806 //
1807 // If this was the only item, then get out
1808 //
1809 if (!ContainsSplit(CmdLine)) {
1810 return (EFI_SUCCESS);
1811 }
1812
1813 //
1814 // recurse to verify the next item
1815 //
1816 TempSpot = FindSplit(CmdLine)+1;
1817 return (VerifySplit(TempSpot));
1818 }
1819
1820 /**
1821 Process a split based operation.
1822
1823 @param[in] CmdLine Pointer to the command line to process
1824 @param[out] ExitStatus The exit status of the command. Ignored if NULL.
1825 Invalid if this function returns an error.
1826
1827 @retval EFI_SUCCESS The operation was successful
1828 @return an error occured.
1829 **/
1830 EFI_STATUS
1831 EFIAPI
1832 ProcessNewSplitCommandLine(
1833 IN CONST CHAR16 *CmdLine,
1834 OUT SHELL_STATUS *ExitStatus
1835 )
1836 {
1837 SPLIT_LIST *Split;
1838 EFI_STATUS Status;
1839
1840 Status = VerifySplit(CmdLine);
1841 if (EFI_ERROR(Status)) {
1842 return (Status);
1843 }
1844
1845 Split = NULL;
1846
1847 //
1848 // are we in an existing split???
1849 //
1850 if (!IsListEmpty(&ShellInfoObject.SplitList.Link)) {
1851 Split = (SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link);
1852 }
1853
1854 if (Split == NULL) {
1855 Status = RunSplitCommand(CmdLine, NULL, NULL, ExitStatus);
1856 } else {
1857 Status = RunSplitCommand(
1858 CmdLine,
1859 Split->SplitStdIn,
1860 Split->SplitStdOut,
1861 ExitStatus
1862 );
1863 }
1864 if (EFI_ERROR(Status)) {
1865 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_SPLIT), ShellInfoObject.HiiHandle, CmdLine);
1866 }
1867 return (Status);
1868 }
1869
1870 /**
1871 Handle a request to change the current file system.
1872
1873 @param[in] CmdLine The passed in command line.
1874
1875 @retval EFI_SUCCESS The operation was successful.
1876 **/
1877 EFI_STATUS
1878 EFIAPI
1879 ChangeMappedDrive(
1880 IN CONST CHAR16 *CmdLine
1881 )
1882 {
1883 EFI_STATUS Status;
1884 Status = EFI_SUCCESS;
1885
1886 //
1887 // make sure we are the right operation
1888 //
1889 ASSERT(CmdLine[(StrLen(CmdLine)-1)] == L':' && StrStr(CmdLine, L" ") == NULL);
1890
1891 //
1892 // Call the protocol API to do the work
1893 //
1894 Status = ShellInfoObject.NewEfiShellProtocol->SetCurDir(NULL, CmdLine);
1895
1896 //
1897 // Report any errors
1898 //
1899 if (EFI_ERROR(Status)) {
1900 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_MAPPING), ShellInfoObject.HiiHandle, CmdLine);
1901 }
1902
1903 return (Status);
1904 }
1905
1906 /**
1907 Reprocess the command line to direct all -? to the help command.
1908
1909 if found, will add "help" as argv[0], and move the rest later.
1910
1911 @param[in,out] CmdLine pointer to the command line to update
1912 **/
1913 EFI_STATUS
1914 EFIAPI
1915 DoHelpUpdate(
1916 IN OUT CHAR16 **CmdLine
1917 )
1918 {
1919 CHAR16 *CurrentParameter;
1920 CHAR16 *Walker;
1921 CHAR16 *LastWalker;
1922 CHAR16 *NewCommandLine;
1923 EFI_STATUS Status;
1924
1925 Status = EFI_SUCCESS;
1926
1927 CurrentParameter = AllocateZeroPool(StrSize(*CmdLine));
1928 if (CurrentParameter == NULL) {
1929 return (EFI_OUT_OF_RESOURCES);
1930 }
1931
1932 Walker = *CmdLine;
1933 while(Walker != NULL && *Walker != CHAR_NULL) {
1934 LastWalker = Walker;
1935 GetNextParameter(&Walker, &CurrentParameter);
1936 if (StrStr(CurrentParameter, L"-?") == CurrentParameter) {
1937 LastWalker[0] = L' ';
1938 LastWalker[1] = L' ';
1939 NewCommandLine = AllocateZeroPool(StrSize(L"help ") + StrSize(*CmdLine));
1940 if (NewCommandLine == NULL) {
1941 Status = EFI_OUT_OF_RESOURCES;
1942 break;
1943 }
1944 StrCpy(NewCommandLine, L"help ");
1945 StrCat(NewCommandLine, *CmdLine);
1946 SHELL_FREE_NON_NULL(*CmdLine);
1947 *CmdLine = NewCommandLine;
1948 break;
1949 }
1950 }
1951
1952 SHELL_FREE_NON_NULL(CurrentParameter);
1953
1954 return (Status);
1955 }
1956
1957 /**
1958 Function to update the shell variable "lasterror".
1959
1960 @param[in] ErrorCode the error code to put into lasterror.
1961 **/
1962 EFI_STATUS
1963 EFIAPI
1964 SetLastError(
1965 IN CONST SHELL_STATUS ErrorCode
1966 )
1967 {
1968 CHAR16 LeString[19];
1969 if (sizeof(EFI_STATUS) == sizeof(UINT64)) {
1970 UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", ErrorCode);
1971 } else {
1972 UnicodeSPrint(LeString, sizeof(LeString), L"0x%x", ErrorCode);
1973 }
1974 DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE););
1975 InternalEfiShellSetEnv(L"lasterror", LeString, TRUE);
1976
1977 return (EFI_SUCCESS);
1978 }
1979
1980 /**
1981 Converts the command line to it's post-processed form. this replaces variables and alias' per UEFI Shell spec.
1982
1983 @param[in,out] CmdLine pointer to the command line to update
1984
1985 @retval EFI_SUCCESS The operation was successful
1986 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
1987 @return some other error occured
1988 **/
1989 EFI_STATUS
1990 EFIAPI
1991 ProcessCommandLineToFinal(
1992 IN OUT CHAR16 **CmdLine
1993 )
1994 {
1995 EFI_STATUS Status;
1996 TrimSpaces(CmdLine);
1997
1998 Status = ShellSubstituteAliases(CmdLine);
1999 if (EFI_ERROR(Status)) {
2000 return (Status);
2001 }
2002
2003 TrimSpaces(CmdLine);
2004
2005 Status = ShellSubstituteVariables(CmdLine);
2006 if (EFI_ERROR(Status)) {
2007 return (Status);
2008 }
2009
2010 TrimSpaces(CmdLine);
2011
2012 //
2013 // update for help parsing
2014 //
2015 if (StrStr(*CmdLine, L"?") != NULL) {
2016 //
2017 // This may do nothing if the ? does not indicate help.
2018 // Save all the details for in the API below.
2019 //
2020 Status = DoHelpUpdate(CmdLine);
2021 }
2022
2023 TrimSpaces(CmdLine);
2024
2025 return (EFI_SUCCESS);
2026 }
2027
2028 /**
2029 Run an internal shell command.
2030
2031 This API will upadate the shell's environment since these commands are libraries.
2032
2033 @param[in] CmdLine the command line to run.
2034 @param[in] FirstParameter the first parameter on the command line
2035 @param[in] ParamProtocol the shell parameters protocol pointer
2036
2037 @param[out] ExitStatus The exit code of the command. Ignored if NULL.
2038
2039 @retval EFI_SUCCESS The command was completed.
2040 @retval EFI_ABORTED The command's operation was aborted.
2041 **/
2042 EFI_STATUS
2043 EFIAPI
2044 RunInternalCommand(
2045 IN CONST CHAR16 *CmdLine,
2046 IN CHAR16 *FirstParameter,
2047 IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol,
2048 OUT SHELL_STATUS *ExitStatus OPTIONAL
2049 )
2050 {
2051 EFI_STATUS Status;
2052 UINTN Argc;
2053 CHAR16 **Argv;
2054 SHELL_STATUS CommandReturnedStatus;
2055 BOOLEAN LastError;
2056
2057 //
2058 // get the argc and argv updated for internal commands
2059 //
2060 Status = UpdateArgcArgv(ParamProtocol, CmdLine, &Argv, &Argc);
2061 if (!EFI_ERROR(Status)) {
2062 //
2063 // Run the internal command.
2064 //
2065 Status = ShellCommandRunCommandHandler(FirstParameter, &CommandReturnedStatus, &LastError);
2066
2067 if (!EFI_ERROR(Status)) {
2068 //
2069 // Update last error status.
2070 // some commands do not update last error.
2071 //
2072 if (LastError) {
2073 SetLastError(CommandReturnedStatus);
2074 }
2075 if (ExitStatus != NULL) {
2076 *ExitStatus = CommandReturnedStatus;
2077 }
2078
2079 //
2080 // Pass thru the exitcode from the app.
2081 //
2082 if (ShellCommandGetExit()) {
2083 //
2084 // An Exit was requested ("exit" command), pass its value up.
2085 //
2086 Status = CommandReturnedStatus;
2087 } else if (CommandReturnedStatus != SHELL_SUCCESS && IsScriptOnlyCommand(FirstParameter)) {
2088 //
2089 // Always abort when a script only command fails for any reason
2090 //
2091 Status = EFI_ABORTED;
2092 } else if (ShellCommandGetCurrentScriptFile() != NULL && CommandReturnedStatus == SHELL_ABORTED) {
2093 //
2094 // Abort when in a script and a command aborted
2095 //
2096 Status = EFI_ABORTED;
2097 }
2098 }
2099 }
2100
2101 //
2102 // This is guarenteed to be called after UpdateArgcArgv no matter what else happened.
2103 // This is safe even if the update API failed. In this case, it may be a no-op.
2104 //
2105 RestoreArgcArgv(ParamProtocol, &Argv, &Argc);
2106
2107 //
2108 // If a script is running and the command is not a scipt only command, then
2109 // change return value to success so the script won't halt (unless aborted).
2110 //
2111 // Script only commands have to be able halt the script since the script will
2112 // not operate if they are failing.
2113 //
2114 if ( ShellCommandGetCurrentScriptFile() != NULL
2115 && !IsScriptOnlyCommand(FirstParameter)
2116 && Status != EFI_ABORTED
2117 ) {
2118 Status = EFI_SUCCESS;
2119 }
2120
2121 return (Status);
2122 }
2123
2124 /**
2125 Function to run the command or file.
2126
2127 @param[in] Type the type of operation being run.
2128 @param[in] CmdLine the command line to run.
2129 @param[in] FirstParameter the first parameter on the command line
2130 @param[in] ParamProtocol the shell parameters protocol pointer
2131
2132 @param[out] ExitStatus The exit code of the command or file.
2133 Ignored if NULL.
2134
2135 @retval EFI_SUCCESS The command was completed.
2136 @retval EFI_ABORTED The command's operation was aborted.
2137 **/
2138 EFI_STATUS
2139 EFIAPI
2140 RunCommandOrFile(
2141 IN SHELL_OPERATION_TYPES Type,
2142 IN CONST CHAR16 *CmdLine,
2143 IN CHAR16 *FirstParameter,
2144 IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol,
2145 OUT SHELL_STATUS *ExitStatus
2146 )
2147 {
2148 EFI_STATUS Status;
2149 CHAR16 *CommandWithPath;
2150 EFI_DEVICE_PATH_PROTOCOL *DevPath;
2151 SHELL_STATUS CalleeExitStatus;
2152
2153 Status = EFI_SUCCESS;
2154 CommandWithPath = NULL;
2155 DevPath = NULL;
2156 CalleeExitStatus = SHELL_INVALID_PARAMETER;
2157
2158 switch (Type) {
2159 case Internal_Command:
2160 Status = RunInternalCommand(
2161 CmdLine,
2162 FirstParameter,
2163 ParamProtocol,
2164 &CalleeExitStatus
2165 );
2166 break;
2167 case Script_File_Name:
2168 case Efi_Application:
2169 //
2170 // Process a fully qualified path
2171 //
2172 if (StrStr(FirstParameter, L":") != NULL) {
2173 ASSERT (CommandWithPath == NULL);
2174 if (ShellIsFile(FirstParameter) == EFI_SUCCESS) {
2175 CommandWithPath = StrnCatGrow(&CommandWithPath, NULL, FirstParameter, 0);
2176 }
2177 }
2178
2179 //
2180 // Process a relative path and also check in the path environment variable
2181 //
2182 if (CommandWithPath == NULL) {
2183 CommandWithPath = ShellFindFilePathEx(FirstParameter, mExecutableExtensions);
2184 }
2185
2186 //
2187 // This should be impossible now.
2188 //
2189 ASSERT(CommandWithPath != NULL);
2190
2191 //
2192 // Make sure that path is not just a directory (or not found)
2193 //
2194 if (!EFI_ERROR(ShellIsDirectory(CommandWithPath))) {
2195 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);
2196 SetLastError(SHELL_NOT_FOUND);
2197 }
2198 switch (Type) {
2199 case Script_File_Name:
2200 Status = RunScriptFile (
2201 CommandWithPath,
2202 NULL,
2203 CmdLine,
2204 ParamProtocol,
2205 &CalleeExitStatus
2206 );
2207 break;
2208 case Efi_Application:
2209 //
2210 // Get the device path of the application image
2211 //
2212 DevPath = ShellInfoObject.NewEfiShellProtocol->GetDevicePathFromFilePath(CommandWithPath);
2213 if (DevPath == NULL){
2214 Status = EFI_OUT_OF_RESOURCES;
2215 break;
2216 }
2217
2218 //
2219 // Execute the device path
2220 //
2221 Status = InternalShellExecuteDevicePath(
2222 &gImageHandle,
2223 DevPath,
2224 CmdLine,
2225 NULL,
2226 NULL,
2227 NULL
2228 );
2229
2230 SHELL_FREE_NON_NULL(DevPath);
2231
2232 if(EFI_ERROR (Status)) {
2233 CalleeExitStatus = (SHELL_STATUS) (Status & (~MAX_BIT));
2234 } else {
2235 CalleeExitStatus = SHELL_SUCCESS;
2236 }
2237
2238 //
2239 // Update last error status.
2240 //
2241 // Status is an EFI_STATUS. Clear top bit to convert to SHELL_STATUS
2242 SetLastError(CalleeExitStatus);
2243 break;
2244 default:
2245 //
2246 // Do nothing.
2247 //
2248 break;
2249 }
2250 break;
2251 default:
2252 //
2253 // Do nothing.
2254 //
2255 break;
2256 }
2257
2258 SHELL_FREE_NON_NULL(CommandWithPath);
2259
2260 if (ExitStatus != NULL) {
2261 *ExitStatus = CalleeExitStatus;
2262 }
2263
2264 return (Status);
2265 }
2266
2267 /**
2268 Function to setup StdIn, StdErr, StdOut, and then run the command or file.
2269
2270 @param[in] Type the type of operation being run.
2271 @param[in] CmdLine the command line to run.
2272 @param[in] FirstParameter the first parameter on the command line.
2273 @param[in] ParamProtocol the shell parameters protocol pointer
2274
2275 @param[out] ExitStatus The exit code of the command or file.
2276 Ignored if NULL.
2277
2278 @retval EFI_SUCCESS The command was completed.
2279 @retval EFI_ABORTED The command's operation was aborted.
2280 **/
2281 EFI_STATUS
2282 EFIAPI
2283 SetupAndRunCommandOrFile(
2284 IN SHELL_OPERATION_TYPES Type,
2285 IN CHAR16 *CmdLine,
2286 IN CHAR16 *FirstParameter,
2287 IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol,
2288 OUT SHELL_STATUS *ExitStatus
2289 )
2290 {
2291 EFI_STATUS Status;
2292 SHELL_FILE_HANDLE OriginalStdIn;
2293 SHELL_FILE_HANDLE OriginalStdOut;
2294 SHELL_FILE_HANDLE OriginalStdErr;
2295 SYSTEM_TABLE_INFO OriginalSystemTableInfo;
2296
2297 //
2298 // Update the StdIn, StdOut, and StdErr for redirection to environment variables, files, etc... unicode and ASCII
2299 //
2300 Status = UpdateStdInStdOutStdErr(ParamProtocol, CmdLine, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);
2301
2302 //
2303 // The StdIn, StdOut, and StdErr are set up.
2304 // Now run the command, script, or application
2305 //
2306 if (!EFI_ERROR(Status)) {
2307 Status = RunCommandOrFile(
2308 Type,
2309 CmdLine,
2310 FirstParameter,
2311 ParamProtocol,
2312 ExitStatus
2313 );
2314 }
2315
2316 //
2317 // Now print errors
2318 //
2319 if (EFI_ERROR(Status)) {
2320 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_ERROR), ShellInfoObject.HiiHandle, (VOID*)(Status));
2321 }
2322
2323 //
2324 // put back the original StdIn, StdOut, and StdErr
2325 //
2326 RestoreStdInStdOutStdErr(ParamProtocol, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);
2327
2328 return (Status);
2329 }
2330
2331 /**
2332 Function will process and run a command line.
2333
2334 This will determine if the command line represents an internal shell
2335 command or dispatch an external application.
2336
2337 @param[in] CmdLine The command line to parse.
2338 @param[out] ExitStatus The exit code of the command. Ignored if NULL.
2339
2340 @retval EFI_SUCCESS The command was completed.
2341 @retval EFI_ABORTED The command's operation was aborted.
2342 **/
2343 EFI_STATUS
2344 EFIAPI
2345 RunCommand(
2346 IN CONST CHAR16 *CmdLine,
2347 OUT SHELL_STATUS *ExitStatus
2348 )
2349 {
2350 EFI_STATUS Status;
2351 CHAR16 *CleanOriginal;
2352 CHAR16 *FirstParameter;
2353 CHAR16 *TempWalker;
2354 SHELL_OPERATION_TYPES Type;
2355
2356 ASSERT(CmdLine != NULL);
2357 if (StrLen(CmdLine) == 0) {
2358 return (EFI_SUCCESS);
2359 }
2360
2361 Status = EFI_SUCCESS;
2362 CleanOriginal = NULL;
2363
2364 CleanOriginal = StrnCatGrow(&CleanOriginal, NULL, CmdLine, 0);
2365 if (CleanOriginal == NULL) {
2366 return (EFI_OUT_OF_RESOURCES);
2367 }
2368
2369 TrimSpaces(&CleanOriginal);
2370
2371 //
2372 // Handle case that passed in command line is just 1 or more " " characters.
2373 //
2374 if (StrLen (CleanOriginal) == 0) {
2375 SHELL_FREE_NON_NULL(CleanOriginal);
2376 return (EFI_SUCCESS);
2377 }
2378
2379 Status = ProcessCommandLineToFinal(&CleanOriginal);
2380 if (EFI_ERROR(Status)) {
2381 SHELL_FREE_NON_NULL(CleanOriginal);
2382 return (Status);
2383 }
2384
2385 //
2386 // We dont do normal processing with a split command line (output from one command input to another)
2387 //
2388 if (ContainsSplit(CleanOriginal)) {
2389 Status = ProcessNewSplitCommandLine(CleanOriginal, ExitStatus);
2390 SHELL_FREE_NON_NULL(CleanOriginal);
2391 return (Status);
2392 }
2393
2394 //
2395 // We need the first parameter information so we can determine the operation type
2396 //
2397 FirstParameter = AllocateZeroPool(StrSize(CleanOriginal));
2398 if (FirstParameter == NULL) {
2399 SHELL_FREE_NON_NULL(CleanOriginal);
2400 return (EFI_OUT_OF_RESOURCES);
2401 }
2402 TempWalker = CleanOriginal;
2403 GetNextParameter(&TempWalker, &FirstParameter);
2404
2405 //
2406 // Depending on the first parameter we change the behavior
2407 //
2408 switch (Type = GetOperationType(FirstParameter)) {
2409 case File_Sys_Change:
2410 Status = ChangeMappedDrive(CleanOriginal);
2411 break;
2412 case Internal_Command:
2413 case Script_File_Name:
2414 case Efi_Application:
2415 Status = SetupAndRunCommandOrFile(
2416 Type,
2417 CleanOriginal,
2418 FirstParameter,
2419 ShellInfoObject.NewShellParametersProtocol,
2420 ExitStatus
2421 );
2422 break;
2423 default:
2424 //
2425 // Whatever was typed, it was invalid.
2426 //
2427 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);
2428 SetLastError(SHELL_NOT_FOUND);
2429 break;
2430 }
2431
2432 SHELL_FREE_NON_NULL(CleanOriginal);
2433 SHELL_FREE_NON_NULL(FirstParameter);
2434
2435 return (Status);
2436 }
2437
2438 STATIC CONST UINT16 InvalidChars[] = {L'*', L'?', L'<', L'>', L'\\', L'/', L'\"', 0x0001, 0x0002};
2439 /**
2440 Function determins if the CommandName COULD be a valid command. It does not determine whether
2441 this is a valid command. It only checks for invalid characters.
2442
2443 @param[in] CommandName The name to check
2444
2445 @retval TRUE CommandName could be a command name
2446 @retval FALSE CommandName could not be a valid command name
2447 **/
2448 BOOLEAN
2449 EFIAPI
2450 IsValidCommandName(
2451 IN CONST CHAR16 *CommandName
2452 )
2453 {
2454 UINTN Count;
2455 if (CommandName == NULL) {
2456 ASSERT(FALSE);
2457 return (FALSE);
2458 }
2459 for ( Count = 0
2460 ; Count < sizeof(InvalidChars) / sizeof(InvalidChars[0])
2461 ; Count++
2462 ){
2463 if (ScanMem16(CommandName, StrSize(CommandName), InvalidChars[Count]) != NULL) {
2464 return (FALSE);
2465 }
2466 }
2467 return (TRUE);
2468 }
2469
2470 /**
2471 Function to process a NSH script file via SHELL_FILE_HANDLE.
2472
2473 @param[in] Handle The handle to the already opened file.
2474 @param[in] Name The name of the script file.
2475
2476 @param[out] ExitStatus The exit code of the script. Ignored if NULL.
2477
2478 @retval EFI_SUCCESS the script completed sucessfully
2479 **/
2480 EFI_STATUS
2481 EFIAPI
2482 RunScriptFileHandle (
2483 IN SHELL_FILE_HANDLE Handle,
2484 IN CONST CHAR16 *Name,
2485 OUT SHELL_STATUS *ExitStatus
2486 )
2487 {
2488 EFI_STATUS Status;
2489 SCRIPT_FILE *NewScriptFile;
2490 UINTN LoopVar;
2491 CHAR16 *CommandLine;
2492 CHAR16 *CommandLine2;
2493 CHAR16 *CommandLine3;
2494 SCRIPT_COMMAND_LIST *LastCommand;
2495 BOOLEAN Ascii;
2496 BOOLEAN PreScriptEchoState;
2497 BOOLEAN PreCommandEchoState;
2498 CONST CHAR16 *CurDir;
2499 UINTN LineCount;
2500 CHAR16 LeString[50];
2501 SHELL_STATUS CalleeExitStatus;
2502
2503 ASSERT(!ShellCommandGetScriptExit());
2504
2505 CalleeExitStatus = SHELL_SUCCESS;
2506
2507 PreScriptEchoState = ShellCommandGetEchoState();
2508
2509 NewScriptFile = (SCRIPT_FILE*)AllocateZeroPool(sizeof(SCRIPT_FILE));
2510 if (NewScriptFile == NULL) {
2511 return (EFI_OUT_OF_RESOURCES);
2512 }
2513
2514 //
2515 // Set up the name
2516 //
2517 ASSERT(NewScriptFile->ScriptName == NULL);
2518 NewScriptFile->ScriptName = StrnCatGrow(&NewScriptFile->ScriptName, NULL, Name, 0);
2519 if (NewScriptFile->ScriptName == NULL) {
2520 DeleteScriptFileStruct(NewScriptFile);
2521 return (EFI_OUT_OF_RESOURCES);
2522 }
2523
2524 //
2525 // Save the parameters (used to replace %0 to %9 later on)
2526 //
2527 NewScriptFile->Argc = ShellInfoObject.NewShellParametersProtocol->Argc;
2528 if (NewScriptFile->Argc != 0) {
2529 NewScriptFile->Argv = (CHAR16**)AllocateZeroPool(NewScriptFile->Argc * sizeof(CHAR16*));
2530 if (NewScriptFile->Argv == NULL) {
2531 DeleteScriptFileStruct(NewScriptFile);
2532 return (EFI_OUT_OF_RESOURCES);
2533 }
2534 for (LoopVar = 0 ; LoopVar < 10 && LoopVar < NewScriptFile->Argc; LoopVar++) {
2535 ASSERT(NewScriptFile->Argv[LoopVar] == NULL);
2536 NewScriptFile->Argv[LoopVar] = StrnCatGrow(&NewScriptFile->Argv[LoopVar], NULL, ShellInfoObject.NewShellParametersProtocol->Argv[LoopVar], 0);
2537 if (NewScriptFile->Argv[LoopVar] == NULL) {
2538 DeleteScriptFileStruct(NewScriptFile);
2539 return (EFI_OUT_OF_RESOURCES);
2540 }
2541 }
2542 } else {
2543 NewScriptFile->Argv = NULL;
2544 }
2545
2546 InitializeListHead(&NewScriptFile->CommandList);
2547 InitializeListHead(&NewScriptFile->SubstList);
2548
2549 //
2550 // Now build the list of all script commands.
2551 //
2552 LineCount = 0;
2553 while(!ShellFileHandleEof(Handle)) {
2554 CommandLine = ShellFileHandleReturnLine(Handle, &Ascii);
2555 LineCount++;
2556 if (CommandLine == NULL || StrLen(CommandLine) == 0 || CommandLine[0] == '#') {
2557 SHELL_FREE_NON_NULL(CommandLine);
2558 continue;
2559 }
2560 NewScriptFile->CurrentCommand = AllocateZeroPool(sizeof(SCRIPT_COMMAND_LIST));
2561 if (NewScriptFile->CurrentCommand == NULL) {
2562 SHELL_FREE_NON_NULL(CommandLine);
2563 DeleteScriptFileStruct(NewScriptFile);
2564 return (EFI_OUT_OF_RESOURCES);
2565 }
2566
2567 NewScriptFile->CurrentCommand->Cl = CommandLine;
2568 NewScriptFile->CurrentCommand->Data = NULL;
2569 NewScriptFile->CurrentCommand->Line = LineCount;
2570
2571 InsertTailList(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);
2572 }
2573
2574 //
2575 // Add this as the topmost script file
2576 //
2577 ShellCommandSetNewScript (NewScriptFile);
2578
2579 //
2580 // Now enumerate through the commands and run each one.
2581 //
2582 CommandLine = AllocateZeroPool(PcdGet16(PcdShellPrintBufferSize));
2583 if (CommandLine == NULL) {
2584 DeleteScriptFileStruct(NewScriptFile);
2585 return (EFI_OUT_OF_RESOURCES);
2586 }
2587 CommandLine2 = AllocateZeroPool(PcdGet16(PcdShellPrintBufferSize));
2588 if (CommandLine2 == NULL) {
2589 FreePool(CommandLine);
2590 DeleteScriptFileStruct(NewScriptFile);
2591 return (EFI_OUT_OF_RESOURCES);
2592 }
2593
2594 for ( NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetFirstNode(&NewScriptFile->CommandList)
2595 ; !IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)
2596 ; // conditional increment in the body of the loop
2597 ){
2598 ASSERT(CommandLine2 != NULL);
2599 StrCpy(CommandLine2, NewScriptFile->CurrentCommand->Cl);
2600
2601 //
2602 // NULL out comments
2603 //
2604 for (CommandLine3 = CommandLine2 ; CommandLine3 != NULL && *CommandLine3 != CHAR_NULL ; CommandLine3++) {
2605 if (*CommandLine3 == L'^') {
2606 if (*(CommandLine3+1) == L'#' || *(CommandLine3+1) == L':') {
2607 CopyMem(CommandLine3, CommandLine3+1, StrSize(CommandLine3) - sizeof(CommandLine3[0]));
2608 }
2609 } else if (*CommandLine3 == L'#') {
2610 *CommandLine3 = CHAR_NULL;
2611 }
2612 }
2613
2614 if (CommandLine2 != NULL && StrLen(CommandLine2) >= 1) {
2615 //
2616 // Due to variability in starting the find and replace action we need to have both buffers the same.
2617 //
2618 StrCpy(CommandLine, CommandLine2);
2619
2620 //
2621 // Remove the %0 to %9 from the command line (if we have some arguments)
2622 //
2623 if (NewScriptFile->Argv != NULL) {
2624 switch (NewScriptFile->Argc) {
2625 default:
2626 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%9", NewScriptFile->Argv[9], FALSE, TRUE);
2627 ASSERT_EFI_ERROR(Status);
2628 case 9:
2629 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%8", NewScriptFile->Argv[8], FALSE, TRUE);
2630 ASSERT_EFI_ERROR(Status);
2631 case 8:
2632 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%7", NewScriptFile->Argv[7], FALSE, TRUE);
2633 ASSERT_EFI_ERROR(Status);
2634 case 7:
2635 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%6", NewScriptFile->Argv[6], FALSE, TRUE);
2636 ASSERT_EFI_ERROR(Status);
2637 case 6:
2638 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%5", NewScriptFile->Argv[5], FALSE, TRUE);
2639 ASSERT_EFI_ERROR(Status);
2640 case 5:
2641 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%4", NewScriptFile->Argv[4], FALSE, TRUE);
2642 ASSERT_EFI_ERROR(Status);
2643 case 4:
2644 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%3", NewScriptFile->Argv[3], FALSE, TRUE);
2645 ASSERT_EFI_ERROR(Status);
2646 case 3:
2647 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%2", NewScriptFile->Argv[2], FALSE, TRUE);
2648 ASSERT_EFI_ERROR(Status);
2649 case 2:
2650 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%1", NewScriptFile->Argv[1], FALSE, TRUE);
2651 ASSERT_EFI_ERROR(Status);
2652 case 1:
2653 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%0", NewScriptFile->Argv[0], FALSE, TRUE);
2654 ASSERT_EFI_ERROR(Status);
2655 break;
2656 case 0:
2657 break;
2658 }
2659 }
2660 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%1", L"\"\"", FALSE, FALSE);
2661 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%2", L"\"\"", FALSE, FALSE);
2662 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%3", L"\"\"", FALSE, FALSE);
2663 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%4", L"\"\"", FALSE, FALSE);
2664 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%5", L"\"\"", FALSE, FALSE);
2665 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%6", L"\"\"", FALSE, FALSE);
2666 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%7", L"\"\"", FALSE, FALSE);
2667 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%8", L"\"\"", FALSE, FALSE);
2668 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%9", L"\"\"", FALSE, FALSE);
2669
2670 StrCpy(CommandLine2, CommandLine);
2671
2672 LastCommand = NewScriptFile->CurrentCommand;
2673
2674 for (CommandLine3 = CommandLine2 ; CommandLine3[0] == L' ' ; CommandLine3++);
2675
2676 if (CommandLine3 != NULL && CommandLine3[0] == L':' ) {
2677 //
2678 // This line is a goto target / label
2679 //
2680 } else {
2681 if (CommandLine3 != NULL && StrLen(CommandLine3) > 0) {
2682 if (CommandLine3[0] == L'@') {
2683 //
2684 // We need to save the current echo state
2685 // and disable echo for just this command.
2686 //
2687 PreCommandEchoState = ShellCommandGetEchoState();
2688 ShellCommandSetEchoState(FALSE);
2689 Status = RunCommand(CommandLine3+1, NULL);
2690
2691 //
2692 // If command was "@echo -off" or "@echo -on" then don't restore echo state
2693 //
2694 if (StrCmp (L"@echo -off", CommandLine3) != 0 &&
2695 StrCmp (L"@echo -on", CommandLine3) != 0) {
2696 //
2697 // Now restore the pre-'@' echo state.
2698 //
2699 ShellCommandSetEchoState(PreCommandEchoState);
2700 }
2701 } else {
2702 if (ShellCommandGetEchoState()) {
2703 CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv(L"cwd");
2704 if (CurDir != NULL && StrLen(CurDir) > 1) {
2705 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CURDIR), ShellInfoObject.HiiHandle, CurDir);
2706 } else {
2707 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_SHELL), ShellInfoObject.HiiHandle);
2708 }
2709 ShellPrintEx(-1, -1, L"%s\r\n", CommandLine2);
2710 }
2711 Status = RunCommand(CommandLine3, NULL);
2712 }
2713 }
2714
2715 if (ShellCommandGetScriptExit()) {
2716 //
2717 // ShellCommandGetExitCode() always returns a UINT64
2718 //
2719 CalleeExitStatus = (SHELL_STATUS) ShellCommandGetExitCode();
2720 UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", CalleeExitStatus);
2721 DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE););
2722 InternalEfiShellSetEnv(L"lasterror", LeString, TRUE);
2723
2724 ShellCommandRegisterExit(FALSE, 0);
2725 Status = EFI_SUCCESS;
2726 break;
2727 }
2728 if (ShellGetExecutionBreakFlag()) {
2729 break;
2730 }
2731 if (EFI_ERROR(Status)) {
2732 CalleeExitStatus = (SHELL_STATUS) Status;
2733 break;
2734 }
2735 if (ShellCommandGetExit()) {
2736 CalleeExitStatus = (SHELL_STATUS) ShellCommandGetExitCode();
2737 break;
2738 }
2739 }
2740 //
2741 // If that commend did not update the CurrentCommand then we need to advance it...
2742 //
2743 if (LastCommand == NewScriptFile->CurrentCommand) {
2744 NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetNextNode(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);
2745 if (!IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)) {
2746 NewScriptFile->CurrentCommand->Reset = TRUE;
2747 }
2748 }
2749 } else {
2750 NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetNextNode(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);
2751 if (!IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)) {
2752 NewScriptFile->CurrentCommand->Reset = TRUE;
2753 }
2754 }
2755 }
2756
2757
2758 FreePool(CommandLine);
2759 FreePool(CommandLine2);
2760 ShellCommandSetNewScript (NULL);
2761
2762 //
2763 // Only if this was the last script reset the state.
2764 //
2765 if (ShellCommandGetCurrentScriptFile()==NULL) {
2766 ShellCommandSetEchoState(PreScriptEchoState);
2767 }
2768
2769 if (ExitStatus != NULL) {
2770 *ExitStatus = CalleeExitStatus;
2771 }
2772
2773 return (EFI_SUCCESS);
2774 }
2775
2776 /**
2777 Function to process a NSH script file.
2778
2779 @param[in] ScriptPath Pointer to the script file name (including file system path).
2780 @param[in] Handle the handle of the script file already opened.
2781 @param[in] CmdLine the command line to run.
2782 @param[in] ParamProtocol the shell parameters protocol pointer
2783
2784 @param[out] ExitStatus The exit code of the script. Ignored if NULL.
2785
2786 @retval EFI_SUCCESS the script completed sucessfully
2787 **/
2788 EFI_STATUS
2789 EFIAPI
2790 RunScriptFile (
2791 IN CONST CHAR16 *ScriptPath,
2792 IN SHELL_FILE_HANDLE Handle OPTIONAL,
2793 IN CONST CHAR16 *CmdLine,
2794 IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol,
2795 OUT SHELL_STATUS *ExitStatus
2796 )
2797 {
2798 EFI_STATUS Status;
2799 SHELL_FILE_HANDLE FileHandle;
2800 UINTN Argc;
2801 CHAR16 **Argv;
2802
2803 if (ShellIsFile(ScriptPath) != EFI_SUCCESS) {
2804 return (EFI_INVALID_PARAMETER);
2805 }
2806
2807 //
2808 // get the argc and argv updated for scripts
2809 //
2810 Status = UpdateArgcArgv(ParamProtocol, CmdLine, &Argv, &Argc);
2811 if (!EFI_ERROR(Status)) {
2812
2813 if (Handle == NULL) {
2814 //
2815 // open the file
2816 //
2817 Status = ShellOpenFileByName(ScriptPath, &FileHandle, EFI_FILE_MODE_READ, 0);
2818 if (!EFI_ERROR(Status)) {
2819 //
2820 // run it
2821 //
2822 Status = RunScriptFileHandle(FileHandle, ScriptPath, ExitStatus);
2823
2824 //
2825 // now close the file
2826 //
2827 ShellCloseFile(&FileHandle);
2828 }
2829 } else {
2830 Status = RunScriptFileHandle(Handle, ScriptPath, ExitStatus);
2831 }
2832 }
2833
2834 //
2835 // This is guarenteed to be called after UpdateArgcArgv no matter what else happened.
2836 // This is safe even if the update API failed. In this case, it may be a no-op.
2837 //
2838 RestoreArgcArgv(ParamProtocol, &Argv, &Argc);
2839
2840 return (Status);
2841 }