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