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