]> git.proxmox.com Git - mirror_edk2.git/blob - ShellPkg/Application/Shell/Shell.c
ShellPkg: typo in the last commit.
[mirror_edk2.git] / ShellPkg / Application / Shell / Shell.c
1 /** @file
2 This is THE shell (application)
3
4 Copyright (c) 2009 - 2013, 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 Find a command line contains a split operation
75
76 @param[in] CmdLine The command line to parse.
77
78 @retval A pointer to the | character in CmdLine or NULL if not present.
79 **/
80 CONST
81 CHAR16*
82 EFIAPI
83 FindSplit(
84 IN CONST CHAR16 *CmdLine
85 )
86 {
87 CONST CHAR16 *TempSpot;
88 TempSpot = NULL;
89 if (StrStr(CmdLine, L"|") != NULL) {
90 for (TempSpot = CmdLine ; TempSpot != NULL && *TempSpot != CHAR_NULL ; TempSpot++) {
91 if (*TempSpot == L'^' && *(TempSpot+1) == L'|') {
92 TempSpot++;
93 } else if (*TempSpot == L'|') {
94 break;
95 }
96 }
97 }
98 return (TempSpot);
99 }
100
101 /**
102 Determine if a command line contains a split operation
103
104 @param[in] CmdLine The command line to parse.
105
106 @retval TRUE CmdLine has a valid split.
107 @retval FALSE CmdLine does not have a valid split.
108 **/
109 BOOLEAN
110 EFIAPI
111 ContainsSplit(
112 IN CONST CHAR16 *CmdLine
113 )
114 {
115 CONST CHAR16 *TempSpot;
116 TempSpot = FindSplit(CmdLine);
117 return (TempSpot != NULL && *TempSpot != CHAR_NULL);
118 }
119
120 /**
121 Function to start monitoring for CTRL-S using SimpleTextInputEx. This
122 feature's enabled state was not known when the shell initially launched.
123
124 @retval EFI_SUCCESS The feature is enabled.
125 @retval EFI_OUT_OF_RESOURCES There is not enough mnemory available.
126 **/
127 EFI_STATUS
128 EFIAPI
129 InternalEfiShellStartCtrlSMonitor(
130 VOID
131 )
132 {
133 EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleEx;
134 EFI_KEY_DATA KeyData;
135 EFI_STATUS Status;
136
137 Status = gBS->OpenProtocol(
138 gST->ConsoleInHandle,
139 &gEfiSimpleTextInputExProtocolGuid,
140 (VOID**)&SimpleEx,
141 gImageHandle,
142 NULL,
143 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
144 if (EFI_ERROR(Status)) {
145 ShellPrintHiiEx(
146 -1,
147 -1,
148 NULL,
149 STRING_TOKEN (STR_SHELL_NO_IN_EX),
150 ShellInfoObject.HiiHandle);
151 return (EFI_SUCCESS);
152 }
153
154 KeyData.KeyState.KeyToggleState = 0;
155 KeyData.Key.ScanCode = 0;
156 KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;
157 KeyData.Key.UnicodeChar = L's';
158
159 Status = SimpleEx->RegisterKeyNotify(
160 SimpleEx,
161 &KeyData,
162 NotificationFunction,
163 &ShellInfoObject.CtrlSNotifyHandle1);
164
165 KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;
166 if (!EFI_ERROR(Status)) {
167 Status = SimpleEx->RegisterKeyNotify(
168 SimpleEx,
169 &KeyData,
170 NotificationFunction,
171 &ShellInfoObject.CtrlSNotifyHandle2);
172 }
173 KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;
174 KeyData.Key.UnicodeChar = 19;
175
176 if (!EFI_ERROR(Status)) {
177 Status = SimpleEx->RegisterKeyNotify(
178 SimpleEx,
179 &KeyData,
180 NotificationFunction,
181 &ShellInfoObject.CtrlSNotifyHandle3);
182 }
183 KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;
184 if (!EFI_ERROR(Status)) {
185 Status = SimpleEx->RegisterKeyNotify(
186 SimpleEx,
187 &KeyData,
188 NotificationFunction,
189 &ShellInfoObject.CtrlSNotifyHandle4);
190 }
191 return (Status);
192 }
193
194
195
196 /**
197 The entry point for the application.
198
199 @param[in] ImageHandle The firmware allocated handle for the EFI image.
200 @param[in] SystemTable A pointer to the EFI System Table.
201
202 @retval EFI_SUCCESS The entry point is executed successfully.
203 @retval other Some error occurs when executing this entry point.
204
205 **/
206 EFI_STATUS
207 EFIAPI
208 UefiMain (
209 IN EFI_HANDLE ImageHandle,
210 IN EFI_SYSTEM_TABLE *SystemTable
211 )
212 {
213 EFI_STATUS Status;
214 CHAR16 *TempString;
215 UINTN Size;
216 EFI_HANDLE ConInHandle;
217 EFI_SIMPLE_TEXT_INPUT_PROTOCOL *OldConIn;
218
219 if (PcdGet8(PcdShellSupportLevel) > 3) {
220 return (EFI_UNSUPPORTED);
221 }
222
223 //
224 // Clear the screen
225 //
226 Status = gST->ConOut->ClearScreen(gST->ConOut);
227 if (EFI_ERROR(Status)) {
228 return (Status);
229 }
230
231 //
232 // Populate the global structure from PCDs
233 //
234 ShellInfoObject.ImageDevPath = NULL;
235 ShellInfoObject.FileDevPath = NULL;
236 ShellInfoObject.PageBreakEnabled = PcdGetBool(PcdShellPageBreakDefault);
237 ShellInfoObject.ViewingSettings.InsertMode = PcdGetBool(PcdShellInsertModeDefault);
238 ShellInfoObject.LogScreenCount = PcdGet8 (PcdShellScreenLogCount );
239
240 //
241 // verify we dont allow for spec violation
242 //
243 ASSERT(ShellInfoObject.LogScreenCount >= 3);
244
245 //
246 // Initialize the LIST ENTRY objects...
247 //
248 InitializeListHead(&ShellInfoObject.BufferToFreeList.Link);
249 InitializeListHead(&ShellInfoObject.ViewingSettings.CommandHistory.Link);
250 InitializeListHead(&ShellInfoObject.SplitList.Link);
251
252 //
253 // Check PCDs for optional features that are not implemented yet.
254 //
255 if ( PcdGetBool(PcdShellSupportOldProtocols)
256 || !FeaturePcdGet(PcdShellRequireHiiPlatform)
257 || FeaturePcdGet(PcdShellSupportFrameworkHii)
258 ) {
259 return (EFI_UNSUPPORTED);
260 }
261
262 //
263 // turn off the watchdog timer
264 //
265 gBS->SetWatchdogTimer (0, 0, 0, NULL);
266
267 //
268 // install our console logger. This will keep a log of the output for back-browsing
269 //
270 Status = ConsoleLoggerInstall(ShellInfoObject.LogScreenCount, &ShellInfoObject.ConsoleInfo);
271 if (!EFI_ERROR(Status)) {
272 //
273 // Enable the cursor to be visible
274 //
275 gST->ConOut->EnableCursor (gST->ConOut, TRUE);
276
277 //
278 // If supporting EFI 1.1 we need to install HII protocol
279 // only do this if PcdShellRequireHiiPlatform == FALSE
280 //
281 // remove EFI_UNSUPPORTED check above when complete.
282 ///@todo add support for Framework HII
283
284 //
285 // install our (solitary) HII package
286 //
287 ShellInfoObject.HiiHandle = HiiAddPackages (&gEfiCallerIdGuid, gImageHandle, ShellStrings, NULL);
288 if (ShellInfoObject.HiiHandle == NULL) {
289 if (PcdGetBool(PcdShellSupportFrameworkHii)) {
290 ///@todo Add our package into Framework HII
291 }
292 if (ShellInfoObject.HiiHandle == NULL) {
293 return (EFI_NOT_STARTED);
294 }
295 }
296
297 //
298 // create and install the EfiShellParametersProtocol
299 //
300 Status = CreatePopulateInstallShellParametersProtocol(&ShellInfoObject.NewShellParametersProtocol, &ShellInfoObject.RootShellInstance);
301 ASSERT_EFI_ERROR(Status);
302 ASSERT(ShellInfoObject.NewShellParametersProtocol != NULL);
303
304 //
305 // create and install the EfiShellProtocol
306 //
307 Status = CreatePopulateInstallShellProtocol(&ShellInfoObject.NewEfiShellProtocol);
308 ASSERT_EFI_ERROR(Status);
309 ASSERT(ShellInfoObject.NewEfiShellProtocol != NULL);
310
311 //
312 // Now initialize the shell library (it requires Shell Parameters protocol)
313 //
314 Status = ShellInitialize();
315 ASSERT_EFI_ERROR(Status);
316
317 Status = CommandInit();
318 ASSERT_EFI_ERROR(Status);
319
320 //
321 // Check the command line
322 //
323 Status = ProcessCommandLine();
324
325 //
326 // If shell support level is >= 1 create the mappings and paths
327 //
328 if (PcdGet8(PcdShellSupportLevel) >= 1) {
329 Status = ShellCommandCreateInitialMappingsAndPaths();
330 }
331
332 //
333 // save the device path for the loaded image and the device path for the filepath (under loaded image)
334 // These are where to look for the startup.nsh file
335 //
336 Status = GetDevicePathsForImageAndFile(&ShellInfoObject.ImageDevPath, &ShellInfoObject.FileDevPath);
337 ASSERT_EFI_ERROR(Status);
338
339 //
340 // Display the version
341 //
342 if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion) {
343 ShellPrintHiiEx (
344 0,
345 gST->ConOut->Mode->CursorRow,
346 NULL,
347 STRING_TOKEN (STR_VER_OUTPUT_MAIN_SHELL),
348 ShellInfoObject.HiiHandle,
349 SupportLevel[PcdGet8(PcdShellSupportLevel)],
350 gEfiShellProtocol->MajorVersion,
351 gEfiShellProtocol->MinorVersion
352 );
353
354 ShellPrintHiiEx (
355 -1,
356 -1,
357 NULL,
358 STRING_TOKEN (STR_VER_OUTPUT_MAIN_SUPPLIER),
359 ShellInfoObject.HiiHandle,
360 (CHAR16 *) PcdGetPtr (PcdShellSupplier)
361 );
362
363 ShellPrintHiiEx (
364 -1,
365 -1,
366 NULL,
367 STRING_TOKEN (STR_VER_OUTPUT_MAIN_UEFI),
368 ShellInfoObject.HiiHandle,
369 (gST->Hdr.Revision&0xffff0000)>>16,
370 (gST->Hdr.Revision&0x0000ffff),
371 gST->FirmwareVendor,
372 gST->FirmwareRevision
373 );
374 }
375
376 //
377 // Display the mapping
378 //
379 if (PcdGet8(PcdShellSupportLevel) >= 2 && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap) {
380 Status = RunCommand(L"map");
381 ASSERT_EFI_ERROR(Status);
382 }
383
384 //
385 // init all the built in alias'
386 //
387 Status = SetBuiltInAlias();
388 ASSERT_EFI_ERROR(Status);
389
390 //
391 // Initialize environment variables
392 //
393 if (ShellCommandGetProfileList() != NULL) {
394 Status = InternalEfiShellSetEnv(L"profiles", ShellCommandGetProfileList(), TRUE);
395 ASSERT_EFI_ERROR(Status);
396 }
397
398 Size = 100;
399 TempString = AllocateZeroPool(Size);
400
401 UnicodeSPrint(TempString, Size, L"%d", PcdGet8(PcdShellSupportLevel));
402 Status = InternalEfiShellSetEnv(L"uefishellsupport", TempString, TRUE);
403 ASSERT_EFI_ERROR(Status);
404
405 UnicodeSPrint(TempString, Size, L"%d.%d", ShellInfoObject.NewEfiShellProtocol->MajorVersion, ShellInfoObject.NewEfiShellProtocol->MinorVersion);
406 Status = InternalEfiShellSetEnv(L"uefishellversion", TempString, TRUE);
407 ASSERT_EFI_ERROR(Status);
408
409 UnicodeSPrint(TempString, Size, L"%d.%d", (gST->Hdr.Revision & 0xFFFF0000) >> 16, gST->Hdr.Revision & 0x0000FFFF);
410 Status = InternalEfiShellSetEnv(L"uefiversion", TempString, TRUE);
411 ASSERT_EFI_ERROR(Status);
412
413 FreePool(TempString);
414
415 if (!EFI_ERROR(Status)) {
416 if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt) {
417 //
418 // Set up the event for CTRL-C monitoring...
419 //
420 Status = InernalEfiShellStartMonitor();
421 }
422
423 if (!EFI_ERROR(Status) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {
424 //
425 // Set up the event for CTRL-S monitoring...
426 //
427 Status = InternalEfiShellStartCtrlSMonitor();
428 }
429
430 if (!EFI_ERROR(Status) && ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {
431 //
432 // close off the gST->ConIn
433 //
434 OldConIn = gST->ConIn;
435 ConInHandle = gST->ConsoleInHandle;
436 gST->ConIn = CreateSimpleTextInOnFile((SHELL_FILE_HANDLE)&FileInterfaceNulFile, &gST->ConsoleInHandle);
437 } else {
438 OldConIn = NULL;
439 ConInHandle = NULL;
440 }
441
442 if (!EFI_ERROR(Status) && PcdGet8(PcdShellSupportLevel) >= 1) {
443 //
444 // process the startup script or launch the called app.
445 //
446 Status = DoStartupScript(ShellInfoObject.ImageDevPath, ShellInfoObject.FileDevPath);
447 }
448
449 if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit && !ShellCommandGetExit() && (PcdGet8(PcdShellSupportLevel) >= 3 || PcdGetBool(PcdShellForceConsole)) && !EFI_ERROR(Status) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {
450 //
451 // begin the UI waiting loop
452 //
453 do {
454 //
455 // clean out all the memory allocated for CONST <something> * return values
456 // between each shell prompt presentation
457 //
458 if (!IsListEmpty(&ShellInfoObject.BufferToFreeList.Link)){
459 FreeBufferList(&ShellInfoObject.BufferToFreeList);
460 }
461
462 //
463 // Reset page break back to default.
464 //
465 ShellInfoObject.PageBreakEnabled = PcdGetBool(PcdShellPageBreakDefault);
466 ShellInfoObject.ConsoleInfo->Enabled = TRUE;
467 ShellInfoObject.ConsoleInfo->RowCounter = 0;
468
469 //
470 // Reset the CTRL-C event (yes we ignore the return values)
471 //
472 Status = gBS->CheckEvent (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak);
473
474 //
475 // Display Prompt
476 //
477 Status = DoShellPrompt();
478 } while (!ShellCommandGetExit());
479 }
480 if (OldConIn != NULL && ConInHandle != NULL) {
481 CloseSimpleTextInOnFile (gST->ConIn);
482 gST->ConIn = OldConIn;
483 gST->ConsoleInHandle = ConInHandle;
484 }
485 }
486 }
487
488 //
489 // uninstall protocols / free memory / etc...
490 //
491 if (ShellInfoObject.UserBreakTimer != NULL) {
492 gBS->CloseEvent(ShellInfoObject.UserBreakTimer);
493 DEBUG_CODE(ShellInfoObject.UserBreakTimer = NULL;);
494 }
495 if (ShellInfoObject.ImageDevPath != NULL) {
496 FreePool(ShellInfoObject.ImageDevPath);
497 DEBUG_CODE(ShellInfoObject.ImageDevPath = NULL;);
498 }
499 if (ShellInfoObject.FileDevPath != NULL) {
500 FreePool(ShellInfoObject.FileDevPath);
501 DEBUG_CODE(ShellInfoObject.FileDevPath = NULL;);
502 }
503 if (ShellInfoObject.NewShellParametersProtocol != NULL) {
504 CleanUpShellParametersProtocol(ShellInfoObject.NewShellParametersProtocol);
505 DEBUG_CODE(ShellInfoObject.NewShellParametersProtocol = NULL;);
506 }
507 if (ShellInfoObject.NewEfiShellProtocol != NULL){
508 if (ShellInfoObject.NewEfiShellProtocol->IsRootShell()){
509 InternalEfiShellSetEnv(L"cwd", NULL, TRUE);
510 }
511 CleanUpShellProtocol(ShellInfoObject.NewEfiShellProtocol);
512 DEBUG_CODE(ShellInfoObject.NewEfiShellProtocol = NULL;);
513 }
514
515 if (!IsListEmpty(&ShellInfoObject.BufferToFreeList.Link)){
516 FreeBufferList(&ShellInfoObject.BufferToFreeList);
517 }
518
519 if (!IsListEmpty(&ShellInfoObject.SplitList.Link)){
520 ASSERT(FALSE); ///@todo finish this de-allocation.
521 }
522
523 if (ShellInfoObject.ShellInitSettings.FileName != NULL) {
524 FreePool(ShellInfoObject.ShellInitSettings.FileName);
525 DEBUG_CODE(ShellInfoObject.ShellInitSettings.FileName = NULL;);
526 }
527
528 if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {
529 FreePool(ShellInfoObject.ShellInitSettings.FileOptions);
530 DEBUG_CODE(ShellInfoObject.ShellInitSettings.FileOptions = NULL;);
531 }
532
533 if (ShellInfoObject.HiiHandle != NULL) {
534 HiiRemovePackages(ShellInfoObject.HiiHandle);
535 DEBUG_CODE(ShellInfoObject.HiiHandle = NULL;);
536 }
537
538 if (!IsListEmpty(&ShellInfoObject.ViewingSettings.CommandHistory.Link)){
539 FreeBufferList(&ShellInfoObject.ViewingSettings.CommandHistory);
540 }
541
542 ASSERT(ShellInfoObject.ConsoleInfo != NULL);
543 if (ShellInfoObject.ConsoleInfo != NULL) {
544 ConsoleLoggerUninstall(ShellInfoObject.ConsoleInfo);
545 FreePool(ShellInfoObject.ConsoleInfo);
546 DEBUG_CODE(ShellInfoObject.ConsoleInfo = NULL;);
547 }
548
549 if (ShellCommandGetExit()) {
550 return ((EFI_STATUS)ShellCommandGetExitCode());
551 }
552 return (Status);
553 }
554
555 /**
556 Sets all the alias' that were registered with the ShellCommandLib library.
557
558 @retval EFI_SUCCESS all init commands were run sucessfully.
559 **/
560 EFI_STATUS
561 EFIAPI
562 SetBuiltInAlias(
563 )
564 {
565 EFI_STATUS Status;
566 CONST ALIAS_LIST *List;
567 ALIAS_LIST *Node;
568
569 //
570 // Get all the commands we want to run
571 //
572 List = ShellCommandGetInitAliasList();
573
574 //
575 // for each command in the List
576 //
577 for ( Node = (ALIAS_LIST*)GetFirstNode(&List->Link)
578 ; !IsNull (&List->Link, &Node->Link)
579 ; Node = (ALIAS_LIST *)GetNextNode(&List->Link, &Node->Link)
580 ){
581 //
582 // install the alias'
583 //
584 Status = InternalSetAlias(Node->CommandString, Node->Alias, TRUE);
585 ASSERT_EFI_ERROR(Status);
586 }
587 return (EFI_SUCCESS);
588 }
589
590 /**
591 Internal function to determine if 2 command names are really the same.
592
593 @param[in] Command1 The pointer to the first command name.
594 @param[in] Command2 The pointer to the second command name.
595
596 @retval TRUE The 2 command names are the same.
597 @retval FALSE The 2 command names are not the same.
598 **/
599 BOOLEAN
600 EFIAPI
601 IsCommand(
602 IN CONST CHAR16 *Command1,
603 IN CONST CHAR16 *Command2
604 )
605 {
606 if (StringNoCaseCompare(&Command1, &Command2) == 0) {
607 return (TRUE);
608 }
609 return (FALSE);
610 }
611
612 /**
613 Internal function to determine if a command is a script only command.
614
615 @param[in] CommandName The pointer to the command name.
616
617 @retval TRUE The command is a script only command.
618 @retval FALSE The command is not a script only command.
619 **/
620 BOOLEAN
621 EFIAPI
622 IsScriptOnlyCommand(
623 IN CONST CHAR16 *CommandName
624 )
625 {
626 if (IsCommand(CommandName, L"for")
627 ||IsCommand(CommandName, L"endfor")
628 ||IsCommand(CommandName, L"if")
629 ||IsCommand(CommandName, L"else")
630 ||IsCommand(CommandName, L"endif")
631 ||IsCommand(CommandName, L"goto")) {
632 return (TRUE);
633 }
634 return (FALSE);
635 }
636
637 /**
638 This function will populate the 2 device path protocol parameters based on the
639 global gImageHandle. The DevPath will point to the device path for the handle that has
640 loaded image protocol installed on it. The FilePath will point to the device path
641 for the file that was loaded.
642
643 @param[in, out] DevPath On a sucessful return the device path to the loaded image.
644 @param[in, out] FilePath On a sucessful return the device path to the file.
645
646 @retval EFI_SUCCESS The 2 device paths were sucessfully returned.
647 @retval other A error from gBS->HandleProtocol.
648
649 @sa HandleProtocol
650 **/
651 EFI_STATUS
652 EFIAPI
653 GetDevicePathsForImageAndFile (
654 IN OUT EFI_DEVICE_PATH_PROTOCOL **DevPath,
655 IN OUT EFI_DEVICE_PATH_PROTOCOL **FilePath
656 )
657 {
658 EFI_STATUS Status;
659 EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
660 EFI_DEVICE_PATH_PROTOCOL *ImageDevicePath;
661
662 ASSERT(DevPath != NULL);
663 ASSERT(FilePath != NULL);
664
665 Status = gBS->OpenProtocol (
666 gImageHandle,
667 &gEfiLoadedImageProtocolGuid,
668 (VOID**)&LoadedImage,
669 gImageHandle,
670 NULL,
671 EFI_OPEN_PROTOCOL_GET_PROTOCOL
672 );
673 if (!EFI_ERROR (Status)) {
674 Status = gBS->OpenProtocol (
675 LoadedImage->DeviceHandle,
676 &gEfiDevicePathProtocolGuid,
677 (VOID**)&ImageDevicePath,
678 gImageHandle,
679 NULL,
680 EFI_OPEN_PROTOCOL_GET_PROTOCOL
681 );
682 if (!EFI_ERROR (Status)) {
683 *DevPath = DuplicateDevicePath (ImageDevicePath);
684 *FilePath = DuplicateDevicePath (LoadedImage->FilePath);
685 gBS->CloseProtocol(
686 LoadedImage->DeviceHandle,
687 &gEfiDevicePathProtocolGuid,
688 gImageHandle,
689 NULL);
690 }
691 gBS->CloseProtocol(
692 gImageHandle,
693 &gEfiLoadedImageProtocolGuid,
694 gImageHandle,
695 NULL);
696 }
697 return (Status);
698 }
699
700 STATIC CONST SHELL_PARAM_ITEM mShellParamList[] = {
701 {L"-nostartup", TypeFlag},
702 {L"-startup", TypeFlag},
703 {L"-noconsoleout", TypeFlag},
704 {L"-noconsolein", TypeFlag},
705 {L"-nointerrupt", TypeFlag},
706 {L"-nomap", TypeFlag},
707 {L"-noversion", TypeFlag},
708 {L"-startup", TypeFlag},
709 {L"-delay", TypeValue},
710 {L"-_exit", TypeFlag},
711 {NULL, TypeMax}
712 };
713
714 /**
715 Process all Uefi Shell 2.0 command line options.
716
717 see Uefi Shell 2.0 section 3.2 for full details.
718
719 the command line must resemble the following:
720
721 shell.efi [ShellOpt-options] [options] [file-name [file-name-options]]
722
723 ShellOpt-options Options which control the initialization behavior of the shell.
724 These options are read from the EFI global variable "ShellOpt"
725 and are processed before options or file-name.
726
727 options Options which control the initialization behavior of the shell.
728
729 file-name The name of a UEFI shell application or script to be executed
730 after initialization is complete. By default, if file-name is
731 specified, then -nostartup is implied. Scripts are not supported
732 by level 0.
733
734 file-name-options The command-line options that are passed to file-name when it
735 is invoked.
736
737 This will initialize the ShellInfoObject.ShellInitSettings global variable.
738
739 @retval EFI_SUCCESS The variable is initialized.
740 **/
741 EFI_STATUS
742 EFIAPI
743 ProcessCommandLine(
744 VOID
745 )
746 {
747 EFI_STATUS Status;
748 LIST_ENTRY *Package;
749 UINTN Size;
750 CONST CHAR16 *TempConst;
751 UINTN Count;
752 UINTN LoopVar;
753 CHAR16 *ProblemParam;
754 UINT64 Intermediate;
755
756 Package = NULL;
757 ProblemParam = NULL;
758
759 Status = ShellCommandLineParse (mShellParamList, &Package, NULL, FALSE);
760
761 Count = 1;
762 Size = 0;
763 TempConst = ShellCommandLineGetRawValue(Package, Count++);
764 if (TempConst != NULL && StrLen(TempConst)) {
765 ShellInfoObject.ShellInitSettings.FileName = AllocateZeroPool(StrSize(TempConst));
766 if (ShellInfoObject.ShellInitSettings.FileName == NULL) {
767 return (EFI_OUT_OF_RESOURCES);
768 }
769 StrCpy(ShellInfoObject.ShellInitSettings.FileName, TempConst);
770 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = 1;
771 for (LoopVar = 0 ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {
772 if (StrCmp(gEfiShellParametersProtocol->Argv[LoopVar], ShellInfoObject.ShellInitSettings.FileName)==0) {
773 LoopVar++;
774 //
775 // We found the file... add the rest of the params...
776 //
777 for ( ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {
778 ASSERT((ShellInfoObject.ShellInitSettings.FileOptions == NULL && Size == 0) || (ShellInfoObject.ShellInitSettings.FileOptions != NULL));
779 StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions,
780 &Size,
781 L" ",
782 0);
783 if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {
784 SHELL_FREE_NON_NULL(ShellInfoObject.ShellInitSettings.FileName);
785 return (EFI_OUT_OF_RESOURCES);
786 }
787 StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions,
788 &Size,
789 gEfiShellParametersProtocol->Argv[LoopVar],
790 0);
791 if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {
792 SHELL_FREE_NON_NULL(ShellInfoObject.ShellInitSettings.FileName);
793 return (EFI_OUT_OF_RESOURCES);
794 }
795 }
796 }
797 }
798 } else {
799 ShellCommandLineFreeVarList(Package);
800 Package = NULL;
801 Status = ShellCommandLineParse (mShellParamList, &Package, &ProblemParam, FALSE);
802 if (EFI_ERROR(Status)) {
803 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), ShellInfoObject.HiiHandle, ProblemParam);
804 FreePool(ProblemParam);
805 ShellCommandLineFreeVarList(Package);
806 return (EFI_INVALID_PARAMETER);
807 }
808 }
809
810 ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup = ShellCommandLineGetFlag(Package, L"-startup");
811 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = ShellCommandLineGetFlag(Package, L"-nostartup");
812 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut = ShellCommandLineGetFlag(Package, L"-noconsoleout");
813 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn = ShellCommandLineGetFlag(Package, L"-noconsolein");
814 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt = ShellCommandLineGetFlag(Package, L"-nointerrupt");
815 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap = ShellCommandLineGetFlag(Package, L"-nomap");
816 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion = ShellCommandLineGetFlag(Package, L"-noversion");
817 ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay = ShellCommandLineGetFlag(Package, L"-delay");
818 ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit = ShellCommandLineGetFlag(Package, L"-_exit");
819
820 ShellInfoObject.ShellInitSettings.Delay = 5;
821
822 if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt) {
823 ShellInfoObject.ShellInitSettings.Delay = 0;
824 } else if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay) {
825 TempConst = ShellCommandLineGetValue(Package, L"-delay");
826 if (TempConst != NULL && *TempConst == L':') {
827 TempConst++;
828 }
829 if (TempConst != NULL && !EFI_ERROR(ShellConvertStringToUint64(TempConst, &Intermediate, FALSE, FALSE))) {
830 ShellInfoObject.ShellInitSettings.Delay = (UINTN)Intermediate;
831 }
832 }
833 ShellCommandLineFreeVarList(Package);
834
835 return (Status);
836 }
837
838 /**
839 Handles all interaction with the default startup script.
840
841 this will check that the correct command line parameters were passed, handle the delay, and then start running the script.
842
843 @param ImagePath the path to the image for shell. first place to look for the startup script
844 @param FilePath the path to the file for shell. second place to look for the startup script.
845
846 @retval EFI_SUCCESS the variable is initialized.
847 **/
848 EFI_STATUS
849 EFIAPI
850 DoStartupScript(
851 EFI_DEVICE_PATH_PROTOCOL *ImagePath,
852 EFI_DEVICE_PATH_PROTOCOL *FilePath
853 )
854 {
855 EFI_STATUS Status;
856 UINTN Delay;
857 EFI_INPUT_KEY Key;
858 SHELL_FILE_HANDLE FileHandle;
859 EFI_DEVICE_PATH_PROTOCOL *NewPath;
860 EFI_DEVICE_PATH_PROTOCOL *NamePath;
861 CHAR16 *FileStringPath;
862 CHAR16 *TempSpot;
863 UINTN NewSize;
864 CONST CHAR16 *MapName;
865
866 Key.UnicodeChar = CHAR_NULL;
867 Key.ScanCode = 0;
868 FileHandle = NULL;
869
870 if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup && ShellInfoObject.ShellInitSettings.FileName != NULL) {
871 //
872 // launch something else instead
873 //
874 NewSize = StrSize(ShellInfoObject.ShellInitSettings.FileName);
875 if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {
876 NewSize += StrSize(ShellInfoObject.ShellInitSettings.FileOptions) + sizeof(CHAR16);
877 }
878 FileStringPath = AllocateZeroPool(NewSize);
879 if (FileStringPath == NULL) {
880 return (EFI_OUT_OF_RESOURCES);
881 }
882 StrCpy(FileStringPath, ShellInfoObject.ShellInitSettings.FileName);
883 if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {
884 StrCat(FileStringPath, L" ");
885 StrCat(FileStringPath, ShellInfoObject.ShellInitSettings.FileOptions);
886 }
887 Status = RunCommand(FileStringPath);
888 FreePool(FileStringPath);
889 return (Status);
890
891 }
892
893 //
894 // for shell level 0 we do no scripts
895 // Without the Startup bit overriding we allow for nostartup to prevent scripts
896 //
897 if ( (PcdGet8(PcdShellSupportLevel) < 1)
898 || (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup)
899 ){
900 return (EFI_SUCCESS);
901 }
902
903 gST->ConOut->EnableCursor(gST->ConOut, FALSE);
904 //
905 // print out our warning and see if they press a key
906 //
907 for ( Status = EFI_UNSUPPORTED, Delay = ShellInfoObject.ShellInitSettings.Delay
908 ; Delay != 0 && EFI_ERROR(Status)
909 ; Delay--
910 ){
911 ShellPrintHiiEx(0, gST->ConOut->Mode->CursorRow, NULL, STRING_TOKEN (STR_SHELL_STARTUP_QUESTION), ShellInfoObject.HiiHandle, Delay);
912 gBS->Stall (1000000);
913 if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {
914 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
915 }
916 }
917 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CRLF), ShellInfoObject.HiiHandle);
918 gST->ConOut->EnableCursor(gST->ConOut, TRUE);
919
920 //
921 // ESC was pressed
922 //
923 if (Status == EFI_SUCCESS && Key.UnicodeChar == 0 && Key.ScanCode == SCAN_ESC) {
924 return (EFI_SUCCESS);
925 }
926
927 //
928 // Try the first location (must be file system)
929 //
930 MapName = ShellInfoObject.NewEfiShellProtocol->GetMapFromDevicePath(&ImagePath);
931 if (MapName != NULL) {
932 FileStringPath = NULL;
933 NewSize = 0;
934 FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, MapName, 0);
935 if (FileStringPath == NULL) {
936 Status = EFI_OUT_OF_RESOURCES;
937 } else {
938 TempSpot = StrStr(FileStringPath, L";");
939 if (TempSpot != NULL) {
940 *TempSpot = CHAR_NULL;
941 }
942 FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, ((FILEPATH_DEVICE_PATH*)FilePath)->PathName, 0);
943 PathRemoveLastItem(FileStringPath);
944 FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, mStartupScript, 0);
945 Status = ShellInfoObject.NewEfiShellProtocol->OpenFileByName(FileStringPath, &FileHandle, EFI_FILE_MODE_READ);
946 FreePool(FileStringPath);
947 }
948 }
949 if (EFI_ERROR(Status)) {
950 NamePath = FileDevicePath (NULL, mStartupScript);
951 NewPath = AppendDevicePathNode (ImagePath, NamePath);
952 FreePool(NamePath);
953
954 //
955 // Try the location
956 //
957 Status = InternalOpenFileDevicePath(NewPath, &FileHandle, EFI_FILE_MODE_READ, 0);
958 FreePool(NewPath);
959 }
960 //
961 // If we got a file, run it
962 //
963 if (!EFI_ERROR(Status) && FileHandle != NULL) {
964 Status = RunScriptFileHandle (FileHandle, mStartupScript);
965 ShellInfoObject.NewEfiShellProtocol->CloseFile(FileHandle);
966 } else {
967 FileStringPath = ShellFindFilePath(mStartupScript);
968 if (FileStringPath == NULL) {
969 //
970 // we return success since we dont need to have a startup script
971 //
972 Status = EFI_SUCCESS;
973 ASSERT(FileHandle == NULL);
974 } else {
975 Status = RunScriptFile(FileStringPath);
976 FreePool(FileStringPath);
977 }
978 }
979
980
981 return (Status);
982 }
983
984 /**
985 Function to perform the shell prompt looping. It will do a single prompt,
986 dispatch the result, and then return. It is expected that the caller will
987 call this function in a loop many times.
988
989 @retval EFI_SUCCESS
990 @retval RETURN_ABORTED
991 **/
992 EFI_STATUS
993 EFIAPI
994 DoShellPrompt (
995 VOID
996 )
997 {
998 UINTN Column;
999 UINTN Row;
1000 CHAR16 *CmdLine;
1001 CONST CHAR16 *CurDir;
1002 UINTN BufferSize;
1003 EFI_STATUS Status;
1004
1005 CurDir = NULL;
1006
1007 //
1008 // Get screen setting to decide size of the command line buffer
1009 //
1010 gST->ConOut->QueryMode (gST->ConOut, gST->ConOut->Mode->Mode, &Column, &Row);
1011 BufferSize = Column * Row * sizeof (CHAR16);
1012 CmdLine = AllocateZeroPool (BufferSize);
1013 if (CmdLine == NULL) {
1014 return EFI_OUT_OF_RESOURCES;
1015 }
1016
1017 CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv(L"cwd");
1018
1019 //
1020 // Prompt for input
1021 //
1022 gST->ConOut->SetCursorPosition (gST->ConOut, 0, gST->ConOut->Mode->CursorRow);
1023
1024 if (CurDir != NULL && StrLen(CurDir) > 1) {
1025 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CURDIR), ShellInfoObject.HiiHandle, CurDir);
1026 } else {
1027 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_SHELL), ShellInfoObject.HiiHandle);
1028 }
1029
1030 //
1031 // Read a line from the console
1032 //
1033 Status = ShellInfoObject.NewEfiShellProtocol->ReadFile(ShellInfoObject.NewShellParametersProtocol->StdIn, &BufferSize, CmdLine);
1034
1035 //
1036 // Null terminate the string and parse it
1037 //
1038 if (!EFI_ERROR (Status)) {
1039 CmdLine[BufferSize / sizeof (CHAR16)] = CHAR_NULL;
1040 Status = RunCommand(CmdLine);
1041 }
1042
1043 //
1044 // Done with this command
1045 //
1046 FreePool (CmdLine);
1047 return Status;
1048 }
1049
1050 /**
1051 Add a buffer to the Buffer To Free List for safely returning buffers to other
1052 places without risking letting them modify internal shell information.
1053
1054 @param Buffer Something to pass to FreePool when the shell is exiting.
1055 **/
1056 VOID*
1057 EFIAPI
1058 AddBufferToFreeList(
1059 VOID *Buffer
1060 )
1061 {
1062 BUFFER_LIST *BufferListEntry;
1063
1064 if (Buffer == NULL) {
1065 return (NULL);
1066 }
1067
1068 BufferListEntry = AllocateZeroPool(sizeof(BUFFER_LIST));
1069 ASSERT(BufferListEntry != NULL);
1070 BufferListEntry->Buffer = Buffer;
1071 InsertTailList(&ShellInfoObject.BufferToFreeList.Link, &BufferListEntry->Link);
1072 return (Buffer);
1073 }
1074
1075 /**
1076 Add a buffer to the Line History List
1077
1078 @param Buffer The line buffer to add.
1079 **/
1080 VOID
1081 EFIAPI
1082 AddLineToCommandHistory(
1083 IN CONST CHAR16 *Buffer
1084 )
1085 {
1086 BUFFER_LIST *Node;
1087
1088 Node = AllocateZeroPool(sizeof(BUFFER_LIST));
1089 ASSERT(Node != NULL);
1090 Node->Buffer = AllocateZeroPool(StrSize(Buffer));
1091 ASSERT(Node->Buffer != NULL);
1092 StrCpy(Node->Buffer, Buffer);
1093
1094 InsertTailList(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link);
1095 }
1096
1097 /**
1098 Checks if a string is an alias for another command. If yes, then it replaces the alias name
1099 with the correct command name.
1100
1101 @param[in, out] CommandString Upon entry the potential alias. Upon return the
1102 command name if it was an alias. If it was not
1103 an alias it will be unchanged. This function may
1104 change the buffer to fit the command name.
1105
1106 @retval EFI_SUCCESS The name was changed.
1107 @retval EFI_SUCCESS The name was not an alias.
1108 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
1109 **/
1110 EFI_STATUS
1111 EFIAPI
1112 ShellConvertAlias(
1113 IN OUT CHAR16 **CommandString
1114 )
1115 {
1116 CONST CHAR16 *NewString;
1117
1118 NewString = ShellInfoObject.NewEfiShellProtocol->GetAlias(*CommandString, NULL);
1119 if (NewString == NULL) {
1120 return (EFI_SUCCESS);
1121 }
1122 FreePool(*CommandString);
1123 *CommandString = AllocateZeroPool(StrSize(NewString));
1124 if (*CommandString == NULL) {
1125 return (EFI_OUT_OF_RESOURCES);
1126 }
1127 StrCpy(*CommandString, NewString);
1128 return (EFI_SUCCESS);
1129 }
1130
1131 /**
1132 Function allocates a new command line and replaces all instances of environment
1133 variable names that are correctly preset to their values.
1134
1135 If the return value is not NULL the memory must be caller freed.
1136
1137 @param[in] OriginalCommandLine The original command line
1138
1139 @retval NULL An error ocurred.
1140 @return The new command line with no environment variables present.
1141 **/
1142 CHAR16*
1143 EFIAPI
1144 ShellConvertVariables (
1145 IN CONST CHAR16 *OriginalCommandLine
1146 )
1147 {
1148 CONST CHAR16 *MasterEnvList;
1149 UINTN NewSize;
1150 CHAR16 *NewCommandLine1;
1151 CHAR16 *NewCommandLine2;
1152 CHAR16 *Temp;
1153 CHAR16 *Temp2;
1154 UINTN ItemSize;
1155 CHAR16 *ItemTemp;
1156 SCRIPT_FILE *CurrentScriptFile;
1157 ALIAS_LIST *AliasListNode;
1158
1159 ASSERT(OriginalCommandLine != NULL);
1160
1161 ItemSize = 0;
1162 NewSize = StrSize(OriginalCommandLine);
1163 CurrentScriptFile = ShellCommandGetCurrentScriptFile();
1164 Temp = NULL;
1165
1166 ///@todo update this to handle the %0 - %9 for scripting only (borrow from line 1256 area) ? ? ?
1167
1168 //
1169 // calculate the size required for the post-conversion string...
1170 //
1171 if (CurrentScriptFile != NULL) {
1172 for (AliasListNode = (ALIAS_LIST*)GetFirstNode(&CurrentScriptFile->SubstList)
1173 ; !IsNull(&CurrentScriptFile->SubstList, &AliasListNode->Link)
1174 ; AliasListNode = (ALIAS_LIST*)GetNextNode(&CurrentScriptFile->SubstList, &AliasListNode->Link)
1175 ){
1176 for (Temp = StrStr(OriginalCommandLine, AliasListNode->Alias)
1177 ; Temp != NULL
1178 ; Temp = StrStr(Temp+1, AliasListNode->Alias)
1179 ){
1180 //
1181 // we need a preceeding and if there is space no ^ preceeding (if no space ignore)
1182 //
1183 if ((((Temp-OriginalCommandLine)>2) && *(Temp-2) != L'^') || ((Temp-OriginalCommandLine)<=2)) {
1184 NewSize += StrSize(AliasListNode->CommandString);
1185 }
1186 }
1187 }
1188 }
1189
1190 for (MasterEnvList = EfiShellGetEnv(NULL)
1191 ; MasterEnvList != NULL && *MasterEnvList != CHAR_NULL //&& *(MasterEnvList+1) != CHAR_NULL
1192 ; MasterEnvList += StrLen(MasterEnvList) + 1
1193 ){
1194 if (StrSize(MasterEnvList) > ItemSize) {
1195 ItemSize = StrSize(MasterEnvList);
1196 }
1197 for (Temp = StrStr(OriginalCommandLine, MasterEnvList)
1198 ; Temp != NULL
1199 ; Temp = StrStr(Temp+1, MasterEnvList)
1200 ){
1201 //
1202 // we need a preceeding and following % and if there is space no ^ preceeding (if no space ignore)
1203 //
1204 if (*(Temp-1) == L'%' && *(Temp+StrLen(MasterEnvList)) == L'%' &&
1205 ((((Temp-OriginalCommandLine)>2) && *(Temp-2) != L'^') || ((Temp-OriginalCommandLine)<=2))) {
1206 NewSize+=StrSize(EfiShellGetEnv(MasterEnvList));
1207 }
1208 }
1209 }
1210
1211 //
1212 // now do the replacements...
1213 //
1214 NewCommandLine1 = AllocateZeroPool(NewSize);
1215 NewCommandLine2 = AllocateZeroPool(NewSize);
1216 ItemTemp = AllocateZeroPool(ItemSize+(2*sizeof(CHAR16)));
1217 if (NewCommandLine1 == NULL || NewCommandLine2 == NULL || ItemTemp == NULL) {
1218 SHELL_FREE_NON_NULL(NewCommandLine1);
1219 SHELL_FREE_NON_NULL(NewCommandLine2);
1220 SHELL_FREE_NON_NULL(ItemTemp);
1221 return (NULL);
1222 }
1223 StrCpy(NewCommandLine1, OriginalCommandLine);
1224 for (MasterEnvList = EfiShellGetEnv(NULL)
1225 ; MasterEnvList != NULL && *MasterEnvList != CHAR_NULL //&& *(MasterEnvList+1) != CHAR_NULL
1226 ; MasterEnvList += StrLen(MasterEnvList) + 1
1227 ){
1228 StrCpy(ItemTemp, L"%");
1229 StrCat(ItemTemp, MasterEnvList);
1230 StrCat(ItemTemp, L"%");
1231 ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, ItemTemp, EfiShellGetEnv(MasterEnvList), TRUE, FALSE);
1232 StrCpy(NewCommandLine1, NewCommandLine2);
1233 }
1234 if (CurrentScriptFile != NULL) {
1235 for (AliasListNode = (ALIAS_LIST*)GetFirstNode(&CurrentScriptFile->SubstList)
1236 ; !IsNull(&CurrentScriptFile->SubstList, &AliasListNode->Link)
1237 ; AliasListNode = (ALIAS_LIST*)GetNextNode(&CurrentScriptFile->SubstList, &AliasListNode->Link)
1238 ){
1239 ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, AliasListNode->Alias, AliasListNode->CommandString, TRUE, FALSE);
1240 StrCpy(NewCommandLine1, NewCommandLine2);
1241 }
1242
1243 //
1244 // Remove non-existant environment variables in scripts only
1245 //
1246 for (Temp = NewCommandLine1 ; Temp != NULL ; ) {
1247 Temp = StrStr(Temp, L"%");
1248 if (Temp == NULL) {
1249 break;
1250 }
1251 while (*(Temp - 1) == L'^') {
1252 Temp = StrStr(Temp + 1, L"%");
1253 if (Temp == NULL) {
1254 break;
1255 }
1256 }
1257 if (Temp == NULL) {
1258 break;
1259 }
1260
1261 Temp2 = StrStr(Temp + 1, L"%");
1262 if (Temp2 == NULL) {
1263 break;
1264 }
1265 while (*(Temp2 - 1) == L'^') {
1266 Temp2 = StrStr(Temp2 + 1, L"%");
1267 if (Temp2 == NULL) {
1268 break;
1269 }
1270 }
1271 if (Temp2 == NULL) {
1272 break;
1273 }
1274
1275 Temp2++;
1276 CopyMem(Temp, Temp2, StrSize(Temp2));
1277 }
1278
1279 }
1280
1281 //
1282 // Now cleanup any straggler intentionally ignored "%" characters
1283 //
1284 ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, L"^%", L"%", TRUE, FALSE);
1285 StrCpy(NewCommandLine1, NewCommandLine2);
1286
1287 FreePool(NewCommandLine2);
1288 FreePool(ItemTemp);
1289
1290 return (NewCommandLine1);
1291 }
1292
1293 /**
1294 Internal function to run a command line with pipe usage.
1295
1296 @param[in] CmdLine The pointer to the command line.
1297 @param[in] StdIn The pointer to the Standard input.
1298 @param[in] StdOut The pointer to the Standard output.
1299
1300 @retval EFI_SUCCESS The split command is executed successfully.
1301 @retval other Some error occurs when executing the split command.
1302 **/
1303 EFI_STATUS
1304 EFIAPI
1305 RunSplitCommand(
1306 IN CONST CHAR16 *CmdLine,
1307 IN SHELL_FILE_HANDLE *StdIn,
1308 IN SHELL_FILE_HANDLE *StdOut
1309 )
1310 {
1311 EFI_STATUS Status;
1312 CHAR16 *NextCommandLine;
1313 CHAR16 *OurCommandLine;
1314 UINTN Size1;
1315 UINTN Size2;
1316 SPLIT_LIST *Split;
1317 SHELL_FILE_HANDLE *TempFileHandle;
1318 BOOLEAN Unicode;
1319
1320 ASSERT(StdOut == NULL);
1321
1322 ASSERT(StrStr(CmdLine, L"|") != NULL);
1323
1324 Status = EFI_SUCCESS;
1325 NextCommandLine = NULL;
1326 OurCommandLine = NULL;
1327 Size1 = 0;
1328 Size2 = 0;
1329
1330 NextCommandLine = StrnCatGrow(&NextCommandLine, &Size1, StrStr(CmdLine, L"|")+1, 0);
1331 OurCommandLine = StrnCatGrow(&OurCommandLine , &Size2, CmdLine , StrStr(CmdLine, L"|") - CmdLine);
1332
1333 if (NextCommandLine == NULL || OurCommandLine == NULL) {
1334 SHELL_FREE_NON_NULL(OurCommandLine);
1335 SHELL_FREE_NON_NULL(NextCommandLine);
1336 return (EFI_OUT_OF_RESOURCES);
1337 } else if (StrStr(OurCommandLine, L"|") != NULL || Size1 == 0 || Size2 == 0) {
1338 SHELL_FREE_NON_NULL(OurCommandLine);
1339 SHELL_FREE_NON_NULL(NextCommandLine);
1340 return (EFI_INVALID_PARAMETER);
1341 } else if (NextCommandLine[0] != CHAR_NULL &&
1342 NextCommandLine[0] == L'a' &&
1343 NextCommandLine[1] == L' '
1344 ){
1345 CopyMem(NextCommandLine, NextCommandLine+1, StrSize(NextCommandLine) - sizeof(NextCommandLine[0]));
1346 Unicode = FALSE;
1347 } else {
1348 Unicode = TRUE;
1349 }
1350
1351
1352 //
1353 // make a SPLIT_LIST item and add to list
1354 //
1355 Split = AllocateZeroPool(sizeof(SPLIT_LIST));
1356 ASSERT(Split != NULL);
1357 Split->SplitStdIn = StdIn;
1358 Split->SplitStdOut = ConvertEfiFileProtocolToShellHandle(CreateFileInterfaceMem(Unicode), NULL);
1359 ASSERT(Split->SplitStdOut != NULL);
1360 InsertHeadList(&ShellInfoObject.SplitList.Link, &Split->Link);
1361
1362 Status = RunCommand(OurCommandLine);
1363
1364 //
1365 // move the output from the first to the in to the second.
1366 //
1367 TempFileHandle = Split->SplitStdOut;
1368 if (Split->SplitStdIn == StdIn) {
1369 Split->SplitStdOut = NULL;
1370 } else {
1371 Split->SplitStdOut = Split->SplitStdIn;
1372 }
1373 Split->SplitStdIn = TempFileHandle;
1374 ShellInfoObject.NewEfiShellProtocol->SetFilePosition(ConvertShellHandleToEfiFileProtocol(Split->SplitStdIn), 0);
1375
1376 if (!EFI_ERROR(Status)) {
1377 Status = RunCommand(NextCommandLine);
1378 }
1379
1380 //
1381 // remove the top level from the ScriptList
1382 //
1383 ASSERT((SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link) == Split);
1384 RemoveEntryList(&Split->Link);
1385
1386 //
1387 // Note that the original StdIn is now the StdOut...
1388 //
1389 if (Split->SplitStdOut != NULL && Split->SplitStdOut != StdIn) {
1390 ShellInfoObject.NewEfiShellProtocol->CloseFile(ConvertShellHandleToEfiFileProtocol(Split->SplitStdOut));
1391 }
1392 if (Split->SplitStdIn != NULL) {
1393 ShellInfoObject.NewEfiShellProtocol->CloseFile(ConvertShellHandleToEfiFileProtocol(Split->SplitStdIn));
1394 }
1395
1396 FreePool(Split);
1397 FreePool(NextCommandLine);
1398 FreePool(OurCommandLine);
1399
1400 return (Status);
1401 }
1402
1403 /**
1404 Function will process and run a command line.
1405
1406 This will determine if the command line represents an internal shell
1407 command or dispatch an external application.
1408
1409 @param[in] CmdLine The command line to parse.
1410
1411 @retval EFI_SUCCESS The command was completed.
1412 @retval EFI_ABORTED The command's operation was aborted.
1413 **/
1414 EFI_STATUS
1415 EFIAPI
1416 RunCommand(
1417 IN CONST CHAR16 *CmdLine
1418 )
1419 {
1420 EFI_STATUS Status;
1421 EFI_STATUS StatusCode;
1422 CHAR16 *CommandName;
1423 SHELL_STATUS ShellStatus;
1424 UINTN Argc;
1425 CHAR16 **Argv;
1426 BOOLEAN LastError;
1427 CHAR16 LeString[19];
1428 CHAR16 *PostAliasCmdLine;
1429 UINTN PostAliasSize;
1430 CHAR16 *PostVariableCmdLine;
1431 CHAR16 *CommandWithPath;
1432 CONST EFI_DEVICE_PATH_PROTOCOL *DevPath;
1433 CONST CHAR16 *TempLocation;
1434 CONST CHAR16 *TempLocation2;
1435 SHELL_FILE_HANDLE OriginalStdIn;
1436 SHELL_FILE_HANDLE OriginalStdOut;
1437 SHELL_FILE_HANDLE OriginalStdErr;
1438 SYSTEM_TABLE_INFO OriginalSystemTableInfo;
1439 UINTN Count;
1440 UINTN Count2;
1441 CHAR16 *CleanOriginal;
1442 SPLIT_LIST *Split;
1443
1444 ASSERT(CmdLine != NULL);
1445 if (StrLen(CmdLine) == 0) {
1446 return (EFI_SUCCESS);
1447 }
1448
1449 CommandName = NULL;
1450 PostVariableCmdLine = NULL;
1451 PostAliasCmdLine = NULL;
1452 CommandWithPath = NULL;
1453 DevPath = NULL;
1454 Status = EFI_SUCCESS;
1455 CleanOriginal = NULL;
1456 Split = NULL;
1457
1458 CleanOriginal = StrnCatGrow(&CleanOriginal, NULL, CmdLine, 0);
1459 if (CleanOriginal == NULL) {
1460 return (EFI_OUT_OF_RESOURCES);
1461 }
1462
1463 //
1464 // Remove any spaces and tabs at the beginning of the string.
1465 //
1466 while ((CleanOriginal[0] == L' ') || (CleanOriginal[0] == L'\t')) {
1467 CopyMem(CleanOriginal, CleanOriginal+1, StrSize(CleanOriginal) - sizeof(CleanOriginal[0]));
1468 }
1469
1470 //
1471 // Handle case that passed in command line is just 1 or more " " characters.
1472 //
1473 if (StrLen (CleanOriginal) == 0) {
1474 if (CleanOriginal != NULL) {
1475 FreePool(CleanOriginal);
1476 CleanOriginal = NULL;
1477 }
1478 return (EFI_SUCCESS);
1479 }
1480
1481 //
1482 // Remove any spaces at the end of the string.
1483 //
1484 while (CleanOriginal[StrLen(CleanOriginal)-1] == L' ') {
1485 CleanOriginal[StrLen(CleanOriginal)-1] = CHAR_NULL;
1486 }
1487
1488 CommandName = NULL;
1489 if (StrStr(CleanOriginal, L" ") == NULL){
1490 StrnCatGrow(&CommandName, NULL, CleanOriginal, 0);
1491 } else {
1492 StrnCatGrow(&CommandName, NULL, CleanOriginal, StrStr(CleanOriginal, L" ") - CleanOriginal);
1493 }
1494
1495 ASSERT(PostAliasCmdLine == NULL);
1496 if (!ShellCommandIsCommandOnList(CommandName)) {
1497 //
1498 // Convert via alias
1499 //
1500 Status = ShellConvertAlias(&CommandName);
1501 PostAliasSize = 0;
1502 PostAliasCmdLine = StrnCatGrow(&PostAliasCmdLine, &PostAliasSize, CommandName, 0);
1503 PostAliasCmdLine = StrnCatGrow(&PostAliasCmdLine, &PostAliasSize, StrStr(CleanOriginal, L" "), 0);
1504 ASSERT_EFI_ERROR(Status);
1505 } else {
1506 PostAliasCmdLine = StrnCatGrow(&PostAliasCmdLine, NULL, CleanOriginal, 0);
1507 }
1508
1509 if (CleanOriginal != NULL) {
1510 FreePool(CleanOriginal);
1511 CleanOriginal = NULL;
1512 }
1513
1514 if (CommandName != NULL) {
1515 FreePool(CommandName);
1516 CommandName = NULL;
1517 }
1518
1519 PostVariableCmdLine = ShellConvertVariables(PostAliasCmdLine);
1520
1521 //
1522 // we can now free the modified by alias command line
1523 //
1524 if (PostAliasCmdLine != NULL) {
1525 FreePool(PostAliasCmdLine);
1526 PostAliasCmdLine = NULL;
1527 }
1528
1529 if (PostVariableCmdLine == NULL) {
1530 return (EFI_OUT_OF_RESOURCES);
1531 }
1532
1533 while (PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] == L' ') {
1534 PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] = CHAR_NULL;
1535 }
1536 while (PostVariableCmdLine[0] == L' ') {
1537 CopyMem(PostVariableCmdLine, PostVariableCmdLine+1, StrSize(PostVariableCmdLine) - sizeof(PostVariableCmdLine[0]));
1538 }
1539
1540 //
1541 // We dont do normal processing with a split command line (output from one command input to another)
1542 //
1543 if (ContainsSplit(PostVariableCmdLine)) {
1544 //
1545 // are we in an existing split???
1546 //
1547 if (!IsListEmpty(&ShellInfoObject.SplitList.Link)) {
1548 Split = (SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link);
1549 }
1550
1551 if (Split == NULL) {
1552 Status = RunSplitCommand(PostVariableCmdLine, NULL, NULL);
1553 } else {
1554 Status = RunSplitCommand(PostVariableCmdLine, Split->SplitStdIn, Split->SplitStdOut);
1555 }
1556 if (EFI_ERROR(Status)) {
1557 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_SPLIT), ShellInfoObject.HiiHandle, PostVariableCmdLine);
1558 }
1559 } else {
1560
1561 //
1562 // If this is a mapped drive change handle that...
1563 //
1564 if (PostVariableCmdLine[(StrLen(PostVariableCmdLine)-1)] == L':' && StrStr(PostVariableCmdLine, L" ") == NULL) {
1565 Status = ShellInfoObject.NewEfiShellProtocol->SetCurDir(NULL, PostVariableCmdLine);
1566 if (EFI_ERROR(Status)) {
1567 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_MAPPING), ShellInfoObject.HiiHandle, PostVariableCmdLine);
1568 }
1569 FreePool(PostVariableCmdLine);
1570 return (Status);
1571 }
1572
1573 ///@todo update this section to divide into 3 ways - run internal command, run split (above), and run an external file...
1574 /// We waste a lot of time doing processing like StdIn,StdOut,Argv,Argc for things that are external files...
1575
1576
1577
1578 Status = UpdateStdInStdOutStdErr(ShellInfoObject.NewShellParametersProtocol, PostVariableCmdLine, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);
1579 if (EFI_ERROR(Status)) {
1580 if (Status == EFI_NOT_FOUND) {
1581 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_REDUNDA_REDIR), ShellInfoObject.HiiHandle);
1582 } else {
1583 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_REDIR), ShellInfoObject.HiiHandle);
1584 }
1585 } else {
1586 while (PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] == L' ') {
1587 PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] = CHAR_NULL;
1588 }
1589 while (PostVariableCmdLine[0] == L' ') {
1590 CopyMem(PostVariableCmdLine, PostVariableCmdLine+1, StrSize(PostVariableCmdLine) - sizeof(PostVariableCmdLine[0]));
1591 }
1592
1593 //
1594 // get the argc and argv updated for internal commands
1595 //
1596 Status = UpdateArgcArgv(ShellInfoObject.NewShellParametersProtocol, PostVariableCmdLine, &Argv, &Argc);
1597 ASSERT_EFI_ERROR(Status);
1598
1599 for (Count = 0 ; Count < ShellInfoObject.NewShellParametersProtocol->Argc ; Count++) {
1600 if (StrStr(ShellInfoObject.NewShellParametersProtocol->Argv[Count], L"-?") == ShellInfoObject.NewShellParametersProtocol->Argv[Count]
1601 || (ShellInfoObject.NewShellParametersProtocol->Argv[0][0] == L'?' && ShellInfoObject.NewShellParametersProtocol->Argv[0][1] == CHAR_NULL)
1602 ) {
1603 //
1604 // We need to redo the arguments since a parameter was -?
1605 // move them all down 1 to the end, then up one then replace the first with help
1606 //
1607 FreePool(ShellInfoObject.NewShellParametersProtocol->Argv[Count]);
1608 ShellInfoObject.NewShellParametersProtocol->Argv[Count] = NULL;
1609 for (Count2 = Count ; (Count2 + 1) < ShellInfoObject.NewShellParametersProtocol->Argc ; Count2++) {
1610 ShellInfoObject.NewShellParametersProtocol->Argv[Count2] = ShellInfoObject.NewShellParametersProtocol->Argv[Count2+1];
1611 }
1612 ShellInfoObject.NewShellParametersProtocol->Argv[Count2] = NULL;
1613 for (Count2 = ShellInfoObject.NewShellParametersProtocol->Argc -1 ; Count2 > 0 ; Count2--) {
1614 ShellInfoObject.NewShellParametersProtocol->Argv[Count2] = ShellInfoObject.NewShellParametersProtocol->Argv[Count2-1];
1615 }
1616 ShellInfoObject.NewShellParametersProtocol->Argv[0] = NULL;
1617 ShellInfoObject.NewShellParametersProtocol->Argv[0] = StrnCatGrow(&ShellInfoObject.NewShellParametersProtocol->Argv[0], NULL, L"help", 0);
1618 break;
1619 }
1620 }
1621
1622 //
1623 // command or file?
1624 //
1625 if (ShellCommandIsCommandOnList(ShellInfoObject.NewShellParametersProtocol->Argv[0])) {
1626 //
1627 // Run the command (which was converted if it was an alias)
1628 //
1629 if (!EFI_ERROR(Status)) {
1630 Status = ShellCommandRunCommandHandler(ShellInfoObject.NewShellParametersProtocol->Argv[0], &ShellStatus, &LastError);
1631 ASSERT_EFI_ERROR(Status);
1632
1633 if (sizeof(EFI_STATUS) == sizeof(UINT64)) {
1634 UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", ShellStatus);
1635 } else {
1636 UnicodeSPrint(LeString, sizeof(LeString), L"0x%x", ShellStatus);
1637 }
1638 DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE););
1639 if (LastError) {
1640 InternalEfiShellSetEnv(L"lasterror", LeString, TRUE);
1641 }
1642 //
1643 // Pass thru the exitcode from the app.
1644 //
1645 if (ShellCommandGetExit()) {
1646 Status = ShellStatus;
1647 } else if (ShellStatus != 0 && IsScriptOnlyCommand(ShellInfoObject.NewShellParametersProtocol->Argv[0])) {
1648 Status = EFI_ABORTED;
1649 }
1650 }
1651 } else {
1652 //
1653 // run an external file (or script)
1654 //
1655 if (StrStr(ShellInfoObject.NewShellParametersProtocol->Argv[0], L":") != NULL) {
1656 ASSERT (CommandWithPath == NULL);
1657 if (ShellIsFile(ShellInfoObject.NewShellParametersProtocol->Argv[0]) == EFI_SUCCESS) {
1658 CommandWithPath = StrnCatGrow(&CommandWithPath, NULL, ShellInfoObject.NewShellParametersProtocol->Argv[0], 0);
1659 }
1660 }
1661 if (CommandWithPath == NULL) {
1662 CommandWithPath = ShellFindFilePathEx(ShellInfoObject.NewShellParametersProtocol->Argv[0], mExecutableExtensions);
1663 }
1664 if (CommandWithPath == NULL || ShellIsDirectory(CommandWithPath) == EFI_SUCCESS) {
1665 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, ShellInfoObject.NewShellParametersProtocol->Argv[0]);
1666
1667 if (sizeof(EFI_STATUS) == sizeof(UINT64)) {
1668 UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", EFI_NOT_FOUND);
1669 } else {
1670 UnicodeSPrint(LeString, sizeof(LeString), L"0x%x", EFI_NOT_FOUND);
1671 }
1672 DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE););
1673 InternalEfiShellSetEnv(L"lasterror", LeString, TRUE);
1674 } else {
1675 //
1676 // Check if it's a NSH (script) file.
1677 //
1678 TempLocation = CommandWithPath+StrLen(CommandWithPath)-4;
1679 TempLocation2 = mScriptExtension;
1680 if ((StrLen(CommandWithPath) > 4) && (StringNoCaseCompare((VOID*)(&TempLocation), (VOID*)(&TempLocation2)) == 0)) {
1681 Status = RunScriptFile (CommandWithPath);
1682 } else {
1683 DevPath = ShellInfoObject.NewEfiShellProtocol->GetDevicePathFromFilePath(CommandWithPath);
1684 ASSERT(DevPath != NULL);
1685 Status = InternalShellExecuteDevicePath(
1686 &gImageHandle,
1687 DevPath,
1688 PostVariableCmdLine,
1689 NULL,
1690 &StatusCode
1691 );
1692
1693 //
1694 // Update last error status.
1695 //
1696 if (sizeof(EFI_STATUS) == sizeof(UINT64)) {
1697 UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", StatusCode);
1698 } else {
1699 UnicodeSPrint(LeString, sizeof(LeString), L"0x%x", StatusCode);
1700 }
1701 DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE););
1702 InternalEfiShellSetEnv(L"lasterror", LeString, TRUE);
1703 }
1704 }
1705 }
1706
1707 //
1708 // Print some error info.
1709 //
1710 if (EFI_ERROR(Status)) {
1711 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_ERROR), ShellInfoObject.HiiHandle, (VOID*)(Status));
1712 }
1713
1714 CommandName = StrnCatGrow(&CommandName, NULL, ShellInfoObject.NewShellParametersProtocol->Argv[0], 0);
1715
1716 RestoreArgcArgv(ShellInfoObject.NewShellParametersProtocol, &Argv, &Argc);
1717
1718 RestoreStdInStdOutStdErr(ShellInfoObject.NewShellParametersProtocol, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);
1719 }
1720 if (CommandName != NULL) {
1721 if (ShellCommandGetCurrentScriptFile() != NULL && !IsScriptOnlyCommand(CommandName)) {
1722 //
1723 // if this is NOT a scipt only command return success so the script won't quit.
1724 // prevent killing the script - this is the only place where we know the actual command name (after alias and variable replacement...)
1725 //
1726 Status = EFI_SUCCESS;
1727 }
1728 }
1729 }
1730
1731 SHELL_FREE_NON_NULL(CommandName);
1732 SHELL_FREE_NON_NULL(CommandWithPath);
1733 SHELL_FREE_NON_NULL(PostVariableCmdLine);
1734
1735 return (Status);
1736 }
1737
1738 STATIC CONST UINT16 InvalidChars[] = {L'*', L'?', L'<', L'>', L'\\', L'/', L'\"', 0x0001, 0x0002};
1739 /**
1740 Function determins if the CommandName COULD be a valid command. It does not determine whether
1741 this is a valid command. It only checks for invalid characters.
1742
1743 @param[in] CommandName The name to check
1744
1745 @retval TRUE CommandName could be a command name
1746 @retval FALSE CommandName could not be a valid command name
1747 **/
1748 BOOLEAN
1749 EFIAPI
1750 IsValidCommandName(
1751 IN CONST CHAR16 *CommandName
1752 )
1753 {
1754 UINTN Count;
1755 if (CommandName == NULL) {
1756 ASSERT(FALSE);
1757 return (FALSE);
1758 }
1759 for ( Count = 0
1760 ; Count < sizeof(InvalidChars) / sizeof(InvalidChars[0])
1761 ; Count++
1762 ){
1763 if (ScanMem16(CommandName, StrSize(CommandName), InvalidChars[Count]) != NULL) {
1764 return (FALSE);
1765 }
1766 }
1767 return (TRUE);
1768 }
1769
1770 /**
1771 Function to process a NSH script file via SHELL_FILE_HANDLE.
1772
1773 @param[in] Handle The handle to the already opened file.
1774 @param[in] Name The name of the script file.
1775
1776 @retval EFI_SUCCESS the script completed sucessfully
1777 **/
1778 EFI_STATUS
1779 EFIAPI
1780 RunScriptFileHandle (
1781 IN SHELL_FILE_HANDLE Handle,
1782 IN CONST CHAR16 *Name
1783 )
1784 {
1785 EFI_STATUS Status;
1786 SCRIPT_FILE *NewScriptFile;
1787 UINTN LoopVar;
1788 CHAR16 *CommandLine;
1789 CHAR16 *CommandLine2;
1790 CHAR16 *CommandLine3;
1791 SCRIPT_COMMAND_LIST *LastCommand;
1792 BOOLEAN Ascii;
1793 BOOLEAN PreScriptEchoState;
1794 BOOLEAN PreCommandEchoState;
1795 CONST CHAR16 *CurDir;
1796 UINTN LineCount;
1797 CHAR16 LeString[50];
1798
1799 ASSERT(!ShellCommandGetScriptExit());
1800
1801 PreScriptEchoState = ShellCommandGetEchoState();
1802
1803 NewScriptFile = (SCRIPT_FILE*)AllocateZeroPool(sizeof(SCRIPT_FILE));
1804 if (NewScriptFile == NULL) {
1805 return (EFI_OUT_OF_RESOURCES);
1806 }
1807
1808 //
1809 // Set up the name
1810 //
1811 ASSERT(NewScriptFile->ScriptName == NULL);
1812 NewScriptFile->ScriptName = StrnCatGrow(&NewScriptFile->ScriptName, NULL, Name, 0);
1813 if (NewScriptFile->ScriptName == NULL) {
1814 DeleteScriptFileStruct(NewScriptFile);
1815 return (EFI_OUT_OF_RESOURCES);
1816 }
1817
1818 //
1819 // Save the parameters (used to replace %0 to %9 later on)
1820 //
1821 NewScriptFile->Argc = ShellInfoObject.NewShellParametersProtocol->Argc;
1822 if (NewScriptFile->Argc != 0) {
1823 NewScriptFile->Argv = (CHAR16**)AllocateZeroPool(NewScriptFile->Argc * sizeof(CHAR16*));
1824 if (NewScriptFile->Argv == NULL) {
1825 DeleteScriptFileStruct(NewScriptFile);
1826 return (EFI_OUT_OF_RESOURCES);
1827 }
1828 for (LoopVar = 0 ; LoopVar < 10 && LoopVar < NewScriptFile->Argc; LoopVar++) {
1829 ASSERT(NewScriptFile->Argv[LoopVar] == NULL);
1830 NewScriptFile->Argv[LoopVar] = StrnCatGrow(&NewScriptFile->Argv[LoopVar], NULL, ShellInfoObject.NewShellParametersProtocol->Argv[LoopVar], 0);
1831 if (NewScriptFile->Argv[LoopVar] == NULL) {
1832 DeleteScriptFileStruct(NewScriptFile);
1833 return (EFI_OUT_OF_RESOURCES);
1834 }
1835 }
1836 } else {
1837 NewScriptFile->Argv = NULL;
1838 }
1839
1840 InitializeListHead(&NewScriptFile->CommandList);
1841 InitializeListHead(&NewScriptFile->SubstList);
1842
1843 //
1844 // Now build the list of all script commands.
1845 //
1846 LineCount = 0;
1847 while(!ShellFileHandleEof(Handle)) {
1848 CommandLine = ShellFileHandleReturnLine(Handle, &Ascii);
1849 LineCount++;
1850 if (CommandLine == NULL || StrLen(CommandLine) == 0) {
1851 continue;
1852 }
1853 NewScriptFile->CurrentCommand = AllocateZeroPool(sizeof(SCRIPT_COMMAND_LIST));
1854 if (NewScriptFile->CurrentCommand == NULL) {
1855 DeleteScriptFileStruct(NewScriptFile);
1856 return (EFI_OUT_OF_RESOURCES);
1857 }
1858
1859 NewScriptFile->CurrentCommand->Cl = CommandLine;
1860 NewScriptFile->CurrentCommand->Data = NULL;
1861 NewScriptFile->CurrentCommand->Line = LineCount;
1862
1863 InsertTailList(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);
1864 }
1865
1866 //
1867 // Add this as the topmost script file
1868 //
1869 ShellCommandSetNewScript (NewScriptFile);
1870
1871 //
1872 // Now enumerate through the commands and run each one.
1873 //
1874 CommandLine = AllocateZeroPool(PcdGet16(PcdShellPrintBufferSize));
1875 if (CommandLine == NULL) {
1876 DeleteScriptFileStruct(NewScriptFile);
1877 return (EFI_OUT_OF_RESOURCES);
1878 }
1879 CommandLine2 = AllocateZeroPool(PcdGet16(PcdShellPrintBufferSize));
1880 if (CommandLine2 == NULL) {
1881 FreePool(CommandLine);
1882 DeleteScriptFileStruct(NewScriptFile);
1883 return (EFI_OUT_OF_RESOURCES);
1884 }
1885
1886 for ( NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetFirstNode(&NewScriptFile->CommandList)
1887 ; !IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)
1888 ; // conditional increment in the body of the loop
1889 ){
1890 ASSERT(CommandLine2 != NULL);
1891 StrCpy(CommandLine2, NewScriptFile->CurrentCommand->Cl);
1892
1893 //
1894 // NULL out comments
1895 //
1896 for (CommandLine3 = CommandLine2 ; CommandLine3 != NULL && *CommandLine3 != CHAR_NULL ; CommandLine3++) {
1897 if (*CommandLine3 == L'^') {
1898 if (*(CommandLine3+1) == L'#' || *(CommandLine3+1) == L':') {
1899 CopyMem(CommandLine3, CommandLine3+1, StrSize(CommandLine3) - sizeof(CommandLine3[0]));
1900 }
1901 } else if (*CommandLine3 == L'#') {
1902 *CommandLine3 = CHAR_NULL;
1903 }
1904 }
1905
1906 if (CommandLine2 != NULL && StrLen(CommandLine2) >= 1) {
1907 //
1908 // Due to variability in starting the find and replace action we need to have both buffers the same.
1909 //
1910 StrCpy(CommandLine, CommandLine2);
1911
1912 //
1913 // Remove the %0 to %9 from the command line (if we have some arguments)
1914 //
1915 if (NewScriptFile->Argv != NULL) {
1916 switch (NewScriptFile->Argc) {
1917 default:
1918 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%9", NewScriptFile->Argv[9], FALSE, TRUE);
1919 ASSERT_EFI_ERROR(Status);
1920 case 9:
1921 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%8", NewScriptFile->Argv[8], FALSE, TRUE);
1922 ASSERT_EFI_ERROR(Status);
1923 case 8:
1924 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%7", NewScriptFile->Argv[7], FALSE, TRUE);
1925 ASSERT_EFI_ERROR(Status);
1926 case 7:
1927 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%6", NewScriptFile->Argv[6], FALSE, TRUE);
1928 ASSERT_EFI_ERROR(Status);
1929 case 6:
1930 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%5", NewScriptFile->Argv[5], FALSE, TRUE);
1931 ASSERT_EFI_ERROR(Status);
1932 case 5:
1933 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%4", NewScriptFile->Argv[4], FALSE, TRUE);
1934 ASSERT_EFI_ERROR(Status);
1935 case 4:
1936 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%3", NewScriptFile->Argv[3], FALSE, TRUE);
1937 ASSERT_EFI_ERROR(Status);
1938 case 3:
1939 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%2", NewScriptFile->Argv[2], FALSE, TRUE);
1940 ASSERT_EFI_ERROR(Status);
1941 case 2:
1942 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%1", NewScriptFile->Argv[1], FALSE, TRUE);
1943 ASSERT_EFI_ERROR(Status);
1944 case 1:
1945 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%0", NewScriptFile->Argv[0], FALSE, TRUE);
1946 ASSERT_EFI_ERROR(Status);
1947 break;
1948 case 0:
1949 break;
1950 }
1951 }
1952 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%1", L"\"\"", FALSE, FALSE);
1953 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%2", L"\"\"", FALSE, FALSE);
1954 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%3", L"\"\"", FALSE, FALSE);
1955 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%4", L"\"\"", FALSE, FALSE);
1956 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%5", L"\"\"", FALSE, FALSE);
1957 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%6", L"\"\"", FALSE, FALSE);
1958 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%7", L"\"\"", FALSE, FALSE);
1959 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%8", L"\"\"", FALSE, FALSE);
1960 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%9", L"\"\"", FALSE, FALSE);
1961
1962 StrCpy(CommandLine2, CommandLine);
1963
1964 LastCommand = NewScriptFile->CurrentCommand;
1965
1966 for (CommandLine3 = CommandLine2 ; CommandLine3[0] == L' ' ; CommandLine3++);
1967
1968 if (CommandLine3 != NULL && CommandLine3[0] == L':' ) {
1969 //
1970 // This line is a goto target / label
1971 //
1972 } else {
1973 if (CommandLine3 != NULL && StrLen(CommandLine3) > 0) {
1974 if (CommandLine3[0] == L'@') {
1975 //
1976 // We need to save the current echo state
1977 // and disable echo for just this command.
1978 //
1979 PreCommandEchoState = ShellCommandGetEchoState();
1980 ShellCommandSetEchoState(FALSE);
1981 Status = RunCommand(CommandLine3+1);
1982
1983 //
1984 // If command was "@echo -off" or "@echo -on" then don't restore echo state
1985 //
1986 if (StrCmp (L"@echo -off", CommandLine3) != 0 &&
1987 StrCmp (L"@echo -on", CommandLine3) != 0) {
1988 //
1989 // Now restore the pre-'@' echo state.
1990 //
1991 ShellCommandSetEchoState(PreCommandEchoState);
1992 }
1993 } else {
1994 if (ShellCommandGetEchoState()) {
1995 CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv(L"cwd");
1996 if (CurDir != NULL && StrLen(CurDir) > 1) {
1997 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CURDIR), ShellInfoObject.HiiHandle, CurDir);
1998 } else {
1999 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_SHELL), ShellInfoObject.HiiHandle);
2000 }
2001 ShellPrintEx(-1, -1, L"%s\r\n", CommandLine2);
2002 }
2003 Status = RunCommand(CommandLine3);
2004 }
2005 }
2006
2007 if (ShellCommandGetScriptExit()) {
2008 //
2009 // ShellCommandGetExitCode() always returns a UINT64
2010 //
2011 UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", ShellCommandGetExitCode());
2012 DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE););
2013 InternalEfiShellSetEnv(L"lasterror", LeString, TRUE);
2014
2015 ShellCommandRegisterExit(FALSE, 0);
2016 Status = EFI_SUCCESS;
2017 break;
2018 }
2019 if (ShellGetExecutionBreakFlag()) {
2020 break;
2021 }
2022 if (EFI_ERROR(Status)) {
2023 break;
2024 }
2025 if (ShellCommandGetExit()) {
2026 break;
2027 }
2028 }
2029 //
2030 // If that commend did not update the CurrentCommand then we need to advance it...
2031 //
2032 if (LastCommand == NewScriptFile->CurrentCommand) {
2033 NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetNextNode(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);
2034 if (!IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)) {
2035 NewScriptFile->CurrentCommand->Reset = TRUE;
2036 }
2037 }
2038 } else {
2039 NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetNextNode(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);
2040 if (!IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)) {
2041 NewScriptFile->CurrentCommand->Reset = TRUE;
2042 }
2043 }
2044 }
2045
2046
2047 FreePool(CommandLine);
2048 FreePool(CommandLine2);
2049 ShellCommandSetNewScript (NULL);
2050
2051 //
2052 // Only if this was the last script reset the state.
2053 //
2054 if (ShellCommandGetCurrentScriptFile()==NULL) {
2055 ShellCommandSetEchoState(PreScriptEchoState);
2056 }
2057 return (EFI_SUCCESS);
2058 }
2059
2060 /**
2061 Function to process a NSH script file.
2062
2063 @param[in] ScriptPath Pointer to the script file name (including file system path).
2064
2065 @retval EFI_SUCCESS the script completed sucessfully
2066 **/
2067 EFI_STATUS
2068 EFIAPI
2069 RunScriptFile (
2070 IN CONST CHAR16 *ScriptPath
2071 )
2072 {
2073 EFI_STATUS Status;
2074 SHELL_FILE_HANDLE FileHandle;
2075
2076 if (ShellIsFile(ScriptPath) != EFI_SUCCESS) {
2077 return (EFI_INVALID_PARAMETER);
2078 }
2079
2080 Status = ShellOpenFileByName(ScriptPath, &FileHandle, EFI_FILE_MODE_READ, 0);
2081 if (EFI_ERROR(Status)) {
2082 return (Status);
2083 }
2084
2085 Status = RunScriptFileHandle(FileHandle, ScriptPath);
2086
2087 ShellCloseFile(&FileHandle);
2088
2089 return (Status);
2090 }