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