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