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