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