]> git.proxmox.com Git - mirror_edk2.git/blob - ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.c
48be39f83242b07dd616fd602d72275c621b8df0
[mirror_edk2.git] / ShellPkg / Library / UefiShellCommandLib / UefiShellCommandLib.c
1 /** @file
2 Provides interface to shell internal functions for shell commands.
3
4 Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14
15 #include "UefiShellCommandLib.h"
16
17 // STATIC local variables
18 STATIC SHELL_COMMAND_INTERNAL_LIST_ENTRY mCommandList;
19 STATIC SCRIPT_FILE_LIST mScriptList;
20 STATIC ALIAS_LIST mAliasList;
21 STATIC BOOLEAN mEchoState;
22 STATIC BOOLEAN mExitRequested;
23 STATIC UINT64 mExitCode;
24 STATIC BOOLEAN mExitScript;
25 STATIC CHAR16 *mProfileList;
26 STATIC UINTN mProfileListSize;
27 STATIC UINTN mFsMaxCount = 0;
28 STATIC UINTN mBlkMaxCount = 0;
29 STATIC BUFFER_LIST mFileHandleList;
30
31 // global variables required by library class.
32 EFI_UNICODE_COLLATION_PROTOCOL *gUnicodeCollation = NULL;
33 SHELL_MAP_LIST gShellMapList;
34 SHELL_MAP_LIST *gShellCurDir = NULL;
35
36 CONST CHAR16* SupportLevel[] = {
37 L"Minimal",
38 L"Scripting",
39 L"Basic",
40 L"Interactive"
41 };
42
43 /**
44 Function to make sure that the global protocol pointers are valid.
45 must be called after constructor before accessing the pointers.
46 **/
47 EFI_STATUS
48 EFIAPI
49 CommandInit(
50 VOID
51 )
52 {
53 EFI_STATUS Status;
54 if (gUnicodeCollation == NULL) {
55 Status = gBS->LocateProtocol(&gEfiUnicodeCollation2ProtocolGuid, NULL, (VOID**)&gUnicodeCollation);
56 if (EFI_ERROR(Status)) {
57 return (EFI_DEVICE_ERROR);
58 }
59 }
60 return (EFI_SUCCESS);
61 }
62
63 /**
64 Constructor for the Shell Command library.
65
66 Initialize the library and determine if the underlying is a UEFI Shell 2.0 or an EFI shell.
67
68 @param ImageHandle the image handle of the process
69 @param SystemTable the EFI System Table pointer
70
71 @retval EFI_SUCCESS the initialization was complete sucessfully
72 **/
73 RETURN_STATUS
74 EFIAPI
75 ShellCommandLibConstructor (
76 IN EFI_HANDLE ImageHandle,
77 IN EFI_SYSTEM_TABLE *SystemTable
78 )
79 {
80 EFI_STATUS Status;
81 InitializeListHead(&gShellMapList.Link);
82 InitializeListHead(&mCommandList.Link);
83 InitializeListHead(&mAliasList.Link);
84 InitializeListHead(&mScriptList.Link);
85 InitializeListHead(&mFileHandleList.Link);
86 mEchoState = TRUE;
87
88 mExitRequested = FALSE;
89 mExitScript = FALSE;
90 mProfileListSize = 0;
91 mProfileList = NULL;
92
93 if (gUnicodeCollation == NULL) {
94 Status = gBS->LocateProtocol(&gEfiUnicodeCollation2ProtocolGuid, NULL, (VOID**)&gUnicodeCollation);
95 if (EFI_ERROR(Status)) {
96 return (EFI_DEVICE_ERROR);
97 }
98 }
99
100 return (RETURN_SUCCESS);
101 }
102
103 /**
104 Frees list of file handles.
105
106 @param[in] List The list to free.
107 **/
108 VOID
109 EFIAPI
110 FreeFileHandleList (
111 IN BUFFER_LIST *List
112 )
113 {
114 BUFFER_LIST *BufferListEntry;
115
116 if (List == NULL){
117 return;
118 }
119 //
120 // enumerate through the buffer list and free all memory
121 //
122 for ( BufferListEntry = ( BUFFER_LIST *)GetFirstNode(&List->Link)
123 ; !IsListEmpty (&List->Link)
124 ; BufferListEntry = (BUFFER_LIST *)GetFirstNode(&List->Link)
125 ){
126 RemoveEntryList(&BufferListEntry->Link);
127 ASSERT(BufferListEntry->Buffer != NULL);
128 SHELL_FREE_NON_NULL(((SHELL_COMMAND_FILE_HANDLE*)(BufferListEntry->Buffer))->Path);
129 SHELL_FREE_NON_NULL(BufferListEntry->Buffer);
130 SHELL_FREE_NON_NULL(BufferListEntry);
131 }
132 }
133
134 /**
135 Destructor for the library. free any resources.
136
137 @param ImageHandle the image handle of the process
138 @param SystemTable the EFI System Table pointer
139
140 @retval RETURN_SUCCESS this function always returns success
141 **/
142 RETURN_STATUS
143 EFIAPI
144 ShellCommandLibDestructor (
145 IN EFI_HANDLE ImageHandle,
146 IN EFI_SYSTEM_TABLE *SystemTable
147 )
148 {
149 SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
150 ALIAS_LIST *Node2;
151 SCRIPT_FILE_LIST *Node3;
152 SHELL_MAP_LIST *MapNode;
153 //
154 // enumerate throught the list and free all the memory
155 //
156 while (!IsListEmpty (&mCommandList.Link)) {
157 Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link);
158 RemoveEntryList(&Node->Link);
159 SHELL_FREE_NON_NULL(Node->CommandString);
160 FreePool(Node);
161 DEBUG_CODE(Node = NULL;);
162 }
163
164 //
165 // enumerate through the alias list and free all memory
166 //
167 while (!IsListEmpty (&mAliasList.Link)) {
168 Node2 = (ALIAS_LIST *)GetFirstNode(&mAliasList.Link);
169 RemoveEntryList(&Node2->Link);
170 SHELL_FREE_NON_NULL(Node2->CommandString);
171 SHELL_FREE_NON_NULL(Node2->Alias);
172 SHELL_FREE_NON_NULL(Node2);
173 DEBUG_CODE(Node2 = NULL;);
174 }
175
176 //
177 // enumerate throught the list and free all the memory
178 //
179 while (!IsListEmpty (&mScriptList.Link)) {
180 Node3 = (SCRIPT_FILE_LIST *)GetFirstNode(&mScriptList.Link);
181 RemoveEntryList(&Node3->Link);
182 DeleteScriptFileStruct(Node3->Data);
183 FreePool(Node3);
184 }
185
186 //
187 // enumerate throught the mappings list and free all the memory
188 //
189 if (!IsListEmpty(&gShellMapList.Link)) {
190 for (MapNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
191 ; !IsListEmpty (&gShellMapList.Link)
192 ; MapNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
193 ){
194 ASSERT(MapNode != NULL);
195 RemoveEntryList(&MapNode->Link);
196 SHELL_FREE_NON_NULL(MapNode->DevicePath);
197 SHELL_FREE_NON_NULL(MapNode->MapName);
198 SHELL_FREE_NON_NULL(MapNode->CurrentDirectoryPath);
199 FreePool(MapNode);
200 }
201 }
202 if (!IsListEmpty(&mFileHandleList.Link)){
203 FreeFileHandleList(&mFileHandleList);
204 }
205
206 if (mProfileList != NULL) {
207 FreePool(mProfileList);
208 }
209
210 gUnicodeCollation = NULL;
211 gShellCurDir = NULL;
212
213 return (RETURN_SUCCESS);
214 }
215
216 /**
217 Checks if a command is already on the list.
218
219 @param[in] CommandString The command string to check for on the list.
220 **/
221 BOOLEAN
222 EFIAPI
223 ShellCommandIsCommandOnList (
224 IN CONST CHAR16 *CommandString
225 )
226 {
227 SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
228
229 //
230 // assert for NULL parameter
231 //
232 ASSERT(CommandString != NULL);
233
234 //
235 // check for the command
236 //
237 for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link)
238 ; !IsNull(&mCommandList.Link, &Node->Link)
239 ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode(&mCommandList.Link, &Node->Link)
240 ){
241 ASSERT(Node->CommandString != NULL);
242 if (gUnicodeCollation->StriColl(
243 gUnicodeCollation,
244 (CHAR16*)CommandString,
245 Node->CommandString) == 0
246 ){
247 return (TRUE);
248 }
249 }
250 return (FALSE);
251 }
252
253 /**
254 Get the help text for a command.
255
256 @param[in] CommandString The command name.
257
258 @retval NULL No help text was found.
259 @return String of help text. Caller reuiqred to free.
260 **/
261 CHAR16*
262 EFIAPI
263 ShellCommandGetCommandHelp (
264 IN CONST CHAR16 *CommandString
265 )
266 {
267 SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
268
269 //
270 // assert for NULL parameter
271 //
272 ASSERT(CommandString != NULL);
273
274 //
275 // check for the command
276 //
277 for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link)
278 ; !IsNull(&mCommandList.Link, &Node->Link)
279 ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode(&mCommandList.Link, &Node->Link)
280 ){
281 ASSERT(Node->CommandString != NULL);
282 if (gUnicodeCollation->StriColl(
283 gUnicodeCollation,
284 (CHAR16*)CommandString,
285 Node->CommandString) == 0
286 ){
287 return (HiiGetString(Node->HiiHandle, Node->ManFormatHelp, NULL));
288 }
289 }
290 return (NULL);
291 }
292
293 /**
294 Registers handlers of type SHELL_RUN_COMMAND and
295 SHELL_GET_MAN_FILENAME for each shell command.
296
297 If the ShellSupportLevel is greater than the value of the
298 PcdShellSupportLevel then return RETURN_UNSUPPORTED.
299
300 Registers the handlers specified by GetHelpInfoHandler and CommandHandler
301 with the command specified by CommandString. If the command named by
302 CommandString has already been registered, then return
303 RETURN_ALREADY_STARTED.
304
305 If there are not enough resources available to register the handlers then
306 RETURN_OUT_OF_RESOURCES is returned.
307
308 If CommandString is NULL, then ASSERT().
309 If GetHelpInfoHandler is NULL, then ASSERT().
310 If CommandHandler is NULL, then ASSERT().
311 If ProfileName is NULL, then ASSERT().
312
313 @param[in] CommandString Pointer to the command name. This is the
314 name to look for on the command line in
315 the shell.
316 @param[in] CommandHandler Pointer to a function that runs the
317 specified command.
318 @param[in] GetManFileName Pointer to a function that provides man
319 filename.
320 @param[in] ShellMinSupportLevel minimum Shell Support Level which has this
321 function.
322 @param[in] ProfileName profile name to require for support of this
323 function.
324 @param[in] CanAffectLE indicates whether this command's return value
325 can change the LASTERROR environment variable.
326 @param[in] HiiHandle Handle of this command's HII entry.
327 @param[in] ManFormatHelp HII locator for the help text.
328
329 @retval RETURN_SUCCESS The handlers were registered.
330 @retval RETURN_OUT_OF_RESOURCES There are not enough resources available to
331 register the shell command.
332 @retval RETURN_UNSUPPORTED the ShellMinSupportLevel was higher than the
333 currently allowed support level.
334 @retval RETURN_ALREADY_STARTED The CommandString represents a command that
335 is already registered. Only 1 handler set for
336 a given command is allowed.
337 @sa SHELL_GET_MAN_FILENAME
338 @sa SHELL_RUN_COMMAND
339 **/
340 RETURN_STATUS
341 EFIAPI
342 ShellCommandRegisterCommandName (
343 IN CONST CHAR16 *CommandString,
344 IN SHELL_RUN_COMMAND CommandHandler,
345 IN SHELL_GET_MAN_FILENAME GetManFileName,
346 IN UINT32 ShellMinSupportLevel,
347 IN CONST CHAR16 *ProfileName,
348 IN CONST BOOLEAN CanAffectLE,
349 IN CONST EFI_HANDLE HiiHandle,
350 IN CONST EFI_STRING_ID ManFormatHelp
351 )
352 {
353 SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
354 SHELL_COMMAND_INTERNAL_LIST_ENTRY *Command;
355 SHELL_COMMAND_INTERNAL_LIST_ENTRY *PrevCommand;
356 INTN LexicalMatchValue;
357
358 //
359 // Initialize local variables.
360 //
361 Command = NULL;
362 PrevCommand = NULL;
363 LexicalMatchValue = 0;
364
365 //
366 // ASSERTs for NULL parameters
367 //
368 ASSERT(CommandString != NULL);
369 ASSERT(GetManFileName != NULL);
370 ASSERT(CommandHandler != NULL);
371 ASSERT(ProfileName != NULL);
372
373 //
374 // check for shell support level
375 //
376 if (PcdGet8(PcdShellSupportLevel) < ShellMinSupportLevel) {
377 return (RETURN_UNSUPPORTED);
378 }
379
380 //
381 // check for already on the list
382 //
383 if (ShellCommandIsCommandOnList(CommandString)) {
384 return (RETURN_ALREADY_STARTED);
385 }
386
387 //
388 // allocate memory for new struct
389 //
390 Node = AllocateZeroPool(sizeof(SHELL_COMMAND_INTERNAL_LIST_ENTRY));
391 ASSERT(Node != NULL);
392 Node->CommandString = AllocateZeroPool(StrSize(CommandString));
393 ASSERT(Node->CommandString != NULL);
394
395 //
396 // populate the new struct
397 //
398 StrCpy(Node->CommandString, CommandString);
399
400 Node->GetManFileName = GetManFileName;
401 Node->CommandHandler = CommandHandler;
402 Node->LastError = CanAffectLE;
403 Node->HiiHandle = HiiHandle;
404 Node->ManFormatHelp = ManFormatHelp;
405
406 if ( StrLen(ProfileName)>0
407 && ((mProfileList != NULL
408 && StrStr(mProfileList, ProfileName) == NULL) || mProfileList == NULL)
409 ){
410 ASSERT((mProfileList == NULL && mProfileListSize == 0) || (mProfileList != NULL));
411 if (mProfileList == NULL) {
412 //
413 // If this is the first make a leading ';'
414 //
415 StrnCatGrow(&mProfileList, &mProfileListSize, L";", 0);
416 }
417 StrnCatGrow(&mProfileList, &mProfileListSize, ProfileName, 0);
418 StrnCatGrow(&mProfileList, &mProfileListSize, L";", 0);
419 }
420
421 //
422 // Insert a new entry on top of the list
423 //
424 InsertHeadList (&mCommandList.Link, &Node->Link);
425
426 //
427 // Move a new registered command to its sorted ordered location in the list
428 //
429 for (Command = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode (&mCommandList.Link),
430 PrevCommand = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode (&mCommandList.Link)
431 ; !IsNull (&mCommandList.Link, &Command->Link)
432 ; Command = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode (&mCommandList.Link, &Command->Link)) {
433
434 //
435 // Get Lexical Comparison Value between PrevCommand and Command list entry
436 //
437 LexicalMatchValue = gUnicodeCollation->StriColl (
438 gUnicodeCollation,
439 PrevCommand->CommandString,
440 Command->CommandString
441 );
442
443 //
444 // Swap PrevCommand and Command list entry if PrevCommand list entry
445 // is alphabetically greater than Command list entry
446 //
447 if (LexicalMatchValue > 0){
448 Command = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *) SwapListEntries (&PrevCommand->Link, &Command->Link);
449 } else if (LexicalMatchValue < 0) {
450 //
451 // PrevCommand entry is lexically lower than Command entry
452 //
453 break;
454 }
455 }
456
457 return (RETURN_SUCCESS);
458 }
459
460 /**
461 Function to get the current Profile string.
462
463 @retval NULL There are no installed profiles.
464 @return A semi-colon delimited list of profiles.
465 **/
466 CONST CHAR16 *
467 EFIAPI
468 ShellCommandGetProfileList (
469 VOID
470 )
471 {
472 return (mProfileList);
473 }
474
475 /**
476 Checks if a command string has been registered for CommandString and if so it runs
477 the previously registered handler for that command with the command line.
478
479 If CommandString is NULL, then ASSERT().
480
481 If Sections is specified, then each section name listed will be compared in a casesensitive
482 manner, to the section names described in Appendix B UEFI Shell 2.0 spec. If the section exists,
483 it will be appended to the returned help text. If the section does not exist, no
484 information will be returned. If Sections is NULL, then all help text information
485 available will be returned.
486
487 @param[in] CommandString Pointer to the command name. This is the name
488 found on the command line in the shell.
489 @param[in, out] RetVal Pointer to the return vaule from the command handler.
490
491 @param[in, out] CanAffectLE indicates whether this command's return value
492 needs to be placed into LASTERROR environment variable.
493
494 @retval RETURN_SUCCESS The handler was run.
495 @retval RETURN_NOT_FOUND The CommandString did not match a registered
496 command name.
497 @sa SHELL_RUN_COMMAND
498 **/
499 RETURN_STATUS
500 EFIAPI
501 ShellCommandRunCommandHandler (
502 IN CONST CHAR16 *CommandString,
503 IN OUT SHELL_STATUS *RetVal,
504 IN OUT BOOLEAN *CanAffectLE OPTIONAL
505 )
506 {
507 SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
508
509 //
510 // assert for NULL parameters
511 //
512 ASSERT(CommandString != NULL);
513
514 //
515 // check for the command
516 //
517 for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link)
518 ; !IsNull(&mCommandList.Link, &Node->Link)
519 ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode(&mCommandList.Link, &Node->Link)
520 ){
521 ASSERT(Node->CommandString != NULL);
522 if (gUnicodeCollation->StriColl(
523 gUnicodeCollation,
524 (CHAR16*)CommandString,
525 Node->CommandString) == 0
526 ){
527 if (CanAffectLE != NULL) {
528 *CanAffectLE = Node->LastError;
529 }
530 if (RetVal != NULL) {
531 *RetVal = Node->CommandHandler(NULL, gST);
532 } else {
533 Node->CommandHandler(NULL, gST);
534 }
535 return (RETURN_SUCCESS);
536 }
537 }
538 return (RETURN_NOT_FOUND);
539 }
540
541 /**
542 Checks if a command string has been registered for CommandString and if so it
543 returns the MAN filename specified for that command.
544
545 If CommandString is NULL, then ASSERT().
546
547 @param[in] CommandString Pointer to the command name. This is the name
548 found on the command line in the shell.\
549
550 @retval NULL the commandString was not a registered command.
551 @return other the name of the MAN file.
552 @sa SHELL_GET_MAN_FILENAME
553 **/
554 CONST CHAR16*
555 EFIAPI
556 ShellCommandGetManFileNameHandler (
557 IN CONST CHAR16 *CommandString
558 )
559 {
560 SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
561
562 //
563 // assert for NULL parameters
564 //
565 ASSERT(CommandString != NULL);
566
567 //
568 // check for the command
569 //
570 for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link)
571 ; !IsNull(&mCommandList.Link, &Node->Link)
572 ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode(&mCommandList.Link, &Node->Link)
573 ){
574 ASSERT(Node->CommandString != NULL);
575 if (gUnicodeCollation->StriColl(
576 gUnicodeCollation,
577 (CHAR16*)CommandString,
578 Node->CommandString) == 0
579 ){
580 return (Node->GetManFileName());
581 }
582 }
583 return (NULL);
584 }
585
586 /**
587 Get the list of all available shell internal commands. This is a linked list
588 (via LIST_ENTRY structure). enumerate through it using the BaseLib linked
589 list functions. do not modify the values.
590
591 @param[in] Sort TRUE to alphabetically sort the values first. FALSE otherwise.
592
593 @return a Linked list of all available shell commands.
594 **/
595 CONST COMMAND_LIST*
596 EFIAPI
597 ShellCommandGetCommandList (
598 IN CONST BOOLEAN Sort
599 )
600 {
601 // if (!Sort) {
602 // return ((COMMAND_LIST*)(&mCommandList));
603 // }
604 return ((COMMAND_LIST*)(&mCommandList));
605 }
606
607 /**
608 Registers aliases to be set as part of the initialization of the shell application.
609
610 If Command is NULL, then ASSERT().
611 If Alias is NULL, then ASSERT().
612
613 @param[in] Command Pointer to the Command
614 @param[in] Alias Pointer to Alias
615
616 @retval RETURN_SUCCESS The handlers were registered.
617 @retval RETURN_OUT_OF_RESOURCES There are not enough resources available to
618 register the shell command.
619 **/
620 RETURN_STATUS
621 EFIAPI
622 ShellCommandRegisterAlias (
623 IN CONST CHAR16 *Command,
624 IN CONST CHAR16 *Alias
625 )
626 {
627 ALIAS_LIST *Node;
628
629 //
630 // Asserts for NULL
631 //
632 ASSERT(Command != NULL);
633 ASSERT(Alias != NULL);
634
635 //
636 // allocate memory for new struct
637 //
638 Node = AllocateZeroPool(sizeof(ALIAS_LIST));
639 ASSERT(Node != NULL);
640 Node->CommandString = AllocateZeroPool(StrSize(Command));
641 Node->Alias = AllocateZeroPool(StrSize(Alias));
642 ASSERT(Node->CommandString != NULL);
643 ASSERT(Node->Alias != NULL);
644
645 //
646 // populate the new struct
647 //
648 StrCpy(Node->CommandString, Command);
649 StrCpy(Node->Alias , Alias );
650
651 //
652 // add the new struct to the list
653 //
654 InsertTailList (&mAliasList.Link, &Node->Link);
655
656 return (RETURN_SUCCESS);
657 }
658
659 /**
660 Get the list of all shell alias commands. This is a linked list
661 (via LIST_ENTRY structure). enumerate through it using the BaseLib linked
662 list functions. do not modify the values.
663
664 @return a Linked list of all requested shell alias'.
665 **/
666 CONST ALIAS_LIST*
667 EFIAPI
668 ShellCommandGetInitAliasList (
669 VOID
670 )
671 {
672 return (&mAliasList);
673 }
674
675 /**
676 Determine if a given alias is on the list of built in alias'.
677
678 @param[in] Alias The alias to test for
679
680 @retval TRUE The alias is a built in alias
681 @retval FALSE The alias is not a built in alias
682 **/
683 BOOLEAN
684 EFIAPI
685 ShellCommandIsOnAliasList(
686 IN CONST CHAR16 *Alias
687 )
688 {
689 ALIAS_LIST *Node;
690
691 //
692 // assert for NULL parameter
693 //
694 ASSERT(Alias != NULL);
695
696 //
697 // check for the Alias
698 //
699 for ( Node = (ALIAS_LIST *)GetFirstNode(&mAliasList.Link)
700 ; !IsNull(&mAliasList.Link, &Node->Link)
701 ; Node = (ALIAS_LIST *)GetNextNode(&mAliasList.Link, &Node->Link)
702 ){
703 ASSERT(Node->CommandString != NULL);
704 ASSERT(Node->Alias != NULL);
705 if (gUnicodeCollation->StriColl(
706 gUnicodeCollation,
707 (CHAR16*)Alias,
708 Node->CommandString) == 0
709 ){
710 return (TRUE);
711 }
712 if (gUnicodeCollation->StriColl(
713 gUnicodeCollation,
714 (CHAR16*)Alias,
715 Node->Alias) == 0
716 ){
717 return (TRUE);
718 }
719 }
720 return (FALSE);
721 }
722
723 /**
724 Function to determine current state of ECHO. Echo determins if lines from scripts
725 and ECHO commands are enabled.
726
727 @retval TRUE Echo is currently enabled
728 @retval FALSE Echo is currently disabled
729 **/
730 BOOLEAN
731 EFIAPI
732 ShellCommandGetEchoState(
733 VOID
734 )
735 {
736 return (mEchoState);
737 }
738
739 /**
740 Function to set current state of ECHO. Echo determins if lines from scripts
741 and ECHO commands are enabled.
742
743 If State is TRUE, Echo will be enabled.
744 If State is FALSE, Echo will be disabled.
745
746 @param[in] State How to set echo.
747 **/
748 VOID
749 EFIAPI
750 ShellCommandSetEchoState(
751 IN BOOLEAN State
752 )
753 {
754 mEchoState = State;
755 }
756
757 /**
758 Indicate that the current shell or script should exit.
759
760 @param[in] ScriptOnly TRUE if exiting a script; FALSE otherwise.
761 @param[in] ErrorCode The 64 bit error code to return.
762 **/
763 VOID
764 EFIAPI
765 ShellCommandRegisterExit (
766 IN BOOLEAN ScriptOnly,
767 IN CONST UINT64 ErrorCode
768 )
769 {
770 mExitRequested = (BOOLEAN)(!mExitRequested);
771 if (mExitRequested) {
772 mExitScript = ScriptOnly;
773 } else {
774 mExitScript = FALSE;
775 }
776 mExitCode = ErrorCode;
777 }
778
779 /**
780 Retrieve the Exit indicator.
781
782 @retval TRUE Exit was indicated.
783 @retval FALSE Exis was not indicated.
784 **/
785 BOOLEAN
786 EFIAPI
787 ShellCommandGetExit (
788 VOID
789 )
790 {
791 return (mExitRequested);
792 }
793
794 /**
795 Retrieve the Exit code.
796
797 If ShellCommandGetExit returns FALSE than the return from this is undefined.
798
799 @return the value passed into RegisterExit.
800 **/
801 UINT64
802 EFIAPI
803 ShellCommandGetExitCode (
804 VOID
805 )
806 {
807 return (mExitCode);
808 }
809 /**
810 Retrieve the Exit script indicator.
811
812 If ShellCommandGetExit returns FALSE than the return from this is undefined.
813
814 @retval TRUE ScriptOnly was indicated.
815 @retval FALSE ScriptOnly was not indicated.
816 **/
817 BOOLEAN
818 EFIAPI
819 ShellCommandGetScriptExit (
820 VOID
821 )
822 {
823 return (mExitScript);
824 }
825
826 /**
827 Function to cleanup all memory from a SCRIPT_FILE structure.
828
829 @param[in] Script The pointer to the structure to cleanup.
830 **/
831 VOID
832 EFIAPI
833 DeleteScriptFileStruct (
834 IN SCRIPT_FILE *Script
835 )
836 {
837 UINT8 LoopVar;
838
839 if (Script == NULL) {
840 return;
841 }
842
843 for (LoopVar = 0 ; LoopVar < Script->Argc ; LoopVar++) {
844 SHELL_FREE_NON_NULL(Script->Argv[LoopVar]);
845 }
846 if (Script->Argv != NULL) {
847 SHELL_FREE_NON_NULL(Script->Argv);
848 }
849 Script->CurrentCommand = NULL;
850 while (!IsListEmpty (&Script->CommandList)) {
851 Script->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetFirstNode(&Script->CommandList);
852 if (Script->CurrentCommand != NULL) {
853 RemoveEntryList(&Script->CurrentCommand->Link);
854 if (Script->CurrentCommand->Cl != NULL) {
855 SHELL_FREE_NON_NULL(Script->CurrentCommand->Cl);
856 }
857 if (Script->CurrentCommand->Data != NULL) {
858 SHELL_FREE_NON_NULL(Script->CurrentCommand->Data);
859 }
860 SHELL_FREE_NON_NULL(Script->CurrentCommand);
861 }
862 }
863 SHELL_FREE_NON_NULL(Script->ScriptName);
864 SHELL_FREE_NON_NULL(Script);
865 }
866
867 /**
868 Function to return a pointer to the currently running script file object.
869
870 @retval NULL A script file is not currently running.
871 @return A pointer to the current script file object.
872 **/
873 SCRIPT_FILE*
874 EFIAPI
875 ShellCommandGetCurrentScriptFile (
876 VOID
877 )
878 {
879 SCRIPT_FILE_LIST *List;
880 if (IsListEmpty (&mScriptList.Link)) {
881 return (NULL);
882 }
883 List = ((SCRIPT_FILE_LIST*)GetFirstNode(&mScriptList.Link));
884 return (List->Data);
885 }
886
887 /**
888 Function to set a new script as the currently running one.
889
890 This function will correctly stack and unstack nested scripts.
891
892 @param[in] Script Pointer to new script information structure. if NULL
893 will remove and de-allocate the top-most Script structure.
894
895 @return A pointer to the current running script file after this
896 change. NULL if removing the final script.
897 **/
898 SCRIPT_FILE*
899 EFIAPI
900 ShellCommandSetNewScript (
901 IN SCRIPT_FILE *Script OPTIONAL
902 )
903 {
904 SCRIPT_FILE_LIST *Node;
905 if (Script == NULL) {
906 if (IsListEmpty (&mScriptList.Link)) {
907 return (NULL);
908 }
909 Node = (SCRIPT_FILE_LIST *)GetFirstNode(&mScriptList.Link);
910 RemoveEntryList(&Node->Link);
911 DeleteScriptFileStruct(Node->Data);
912 FreePool(Node);
913 } else {
914 Node = AllocateZeroPool(sizeof(SCRIPT_FILE_LIST));
915 if (Node == NULL) {
916 return (NULL);
917 }
918 Node->Data = Script;
919 InsertHeadList(&mScriptList.Link, &Node->Link);
920 }
921 return (ShellCommandGetCurrentScriptFile());
922 }
923
924 /**
925 Function to generate the next default mapping name.
926
927 If the return value is not NULL then it must be callee freed.
928
929 @param Type What kind of mapping name to make.
930
931 @retval NULL a memory allocation failed.
932 @return a new map name string
933 **/
934 CHAR16*
935 EFIAPI
936 ShellCommandCreateNewMappingName(
937 IN CONST SHELL_MAPPING_TYPE Type
938 )
939 {
940 CHAR16 *String;
941 ASSERT(Type < MappingTypeMax);
942
943 String = NULL;
944
945 String = AllocateZeroPool(PcdGet8(PcdShellMapNameLength) * sizeof(String[0]));
946 UnicodeSPrint(
947 String,
948 PcdGet8(PcdShellMapNameLength) * sizeof(String[0]),
949 Type == MappingTypeFileSystem?L"FS%d:":L"BLK%d:",
950 Type == MappingTypeFileSystem?mFsMaxCount++:mBlkMaxCount++);
951
952 return (String);
953 }
954
955 /**
956 Function to add a map node to the list of map items and update the "path" environment variable (optionally).
957
958 If Path is TRUE (during initialization only), the path environment variable will also be updated to include
959 default paths on the new map name...
960
961 Path should be FALSE when this function is called from the protocol SetMap function.
962
963 @param[in] Name The human readable mapped name.
964 @param[in] DevicePath The Device Path for this map.
965 @param[in] Flags The Flags attribute for this map item.
966 @param[in] Path TRUE to update path, FALSE to skip this step (should only be TRUE during initialization).
967
968 @retval EFI_SUCCESS The addition was sucessful.
969 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
970 @retval EFI_INVALID_PARAMETER A parameter was invalid.
971 **/
972 EFI_STATUS
973 EFIAPI
974 ShellCommandAddMapItemAndUpdatePath(
975 IN CONST CHAR16 *Name,
976 IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath,
977 IN CONST UINT64 Flags,
978 IN CONST BOOLEAN Path
979 )
980 {
981 EFI_STATUS Status;
982 SHELL_MAP_LIST *MapListNode;
983 CONST CHAR16 *OriginalPath;
984 CHAR16 *NewPath;
985 UINTN NewPathSize;
986
987 NewPathSize = 0;
988 NewPath = NULL;
989 OriginalPath = NULL;
990 Status = EFI_SUCCESS;
991
992 MapListNode = AllocateZeroPool(sizeof(SHELL_MAP_LIST));
993 if (MapListNode == NULL) {
994 Status = EFI_OUT_OF_RESOURCES;
995 } else {
996 MapListNode->Flags = Flags;
997 MapListNode->MapName = AllocateZeroPool(StrSize(Name));
998 MapListNode->DevicePath = DuplicateDevicePath(DevicePath);
999 if ((MapListNode->MapName == NULL) || (MapListNode->DevicePath == NULL)){
1000 Status = EFI_OUT_OF_RESOURCES;
1001 } else {
1002 StrCpy(MapListNode->MapName, Name);
1003 InsertTailList(&gShellMapList.Link, &MapListNode->Link);
1004 }
1005 }
1006 if (EFI_ERROR(Status)) {
1007 if (MapListNode != NULL) {
1008 if (MapListNode->DevicePath != NULL) {
1009 FreePool(MapListNode->DevicePath);
1010 }
1011 if (MapListNode->MapName != NULL) {
1012 FreePool(MapListNode->MapName);
1013 }
1014 FreePool(MapListNode);
1015 }
1016 } else if (Path) {
1017 //
1018 // Since there was no error and Path was TRUE
1019 // Now add the correct path for that mapping
1020 //
1021 OriginalPath = gEfiShellProtocol->GetEnv(L"path");
1022 ASSERT((NewPath == NULL && NewPathSize == 0) || (NewPath != NULL));
1023 if (OriginalPath != NULL) {
1024 StrnCatGrow(&NewPath, &NewPathSize, OriginalPath, 0);
1025 } else {
1026 StrnCatGrow(&NewPath, &NewPathSize, L".\\", 0);
1027 }
1028 StrnCatGrow(&NewPath, &NewPathSize, L";", 0);
1029 StrnCatGrow(&NewPath, &NewPathSize, Name, 0);
1030 StrnCatGrow(&NewPath, &NewPathSize, L"\\efi\\tools\\;", 0);
1031 StrnCatGrow(&NewPath, &NewPathSize, Name, 0);
1032 StrnCatGrow(&NewPath, &NewPathSize, L"\\efi\\boot\\;", 0);
1033 StrnCatGrow(&NewPath, &NewPathSize, Name, 0);
1034 StrnCatGrow(&NewPath, &NewPathSize, L"\\", 0);
1035
1036 Status = gEfiShellProtocol->SetEnv(L"path", NewPath, TRUE);
1037 ASSERT_EFI_ERROR(Status);
1038 FreePool(NewPath);
1039 }
1040 return (Status);
1041 }
1042
1043 /**
1044 Creates the default map names for each device path in the system with
1045 a protocol depending on the Type.
1046
1047 Creates the consistent map names for each device path in the system with
1048 a protocol depending on the Type.
1049
1050 Note: This will reset all mappings in the system("map -r").
1051
1052 Also sets up the default path environment variable if Type is FileSystem.
1053
1054 @retval EFI_SUCCESS All map names were created sucessfully.
1055 @retval EFI_NOT_FOUND No protocols were found in the system.
1056 @return Error returned from gBS->LocateHandle().
1057
1058 @sa LocateHandle
1059 **/
1060 EFI_STATUS
1061 EFIAPI
1062 ShellCommandCreateInitialMappingsAndPaths(
1063 VOID
1064 )
1065 {
1066 EFI_STATUS Status;
1067 EFI_HANDLE *HandleList;
1068 UINTN Count;
1069 EFI_DEVICE_PATH_PROTOCOL **DevicePathList;
1070 CHAR16 *NewDefaultName;
1071 CHAR16 *NewConsistName;
1072 EFI_DEVICE_PATH_PROTOCOL **ConsistMappingTable;
1073 SHELL_MAP_LIST *MapListNode;
1074
1075 HandleList = NULL;
1076
1077 //
1078 // Reset the static members back to zero
1079 //
1080 mFsMaxCount = 0;
1081 mBlkMaxCount = 0;
1082
1083 gEfiShellProtocol->SetEnv(L"path", L"", TRUE);
1084
1085 //
1086 // First empty out the existing list.
1087 //
1088 if (!IsListEmpty(&gShellMapList.Link)) {
1089 for ( MapListNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
1090 ; !IsListEmpty(&gShellMapList.Link)
1091 ; MapListNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
1092 ){
1093 RemoveEntryList(&MapListNode->Link);
1094 FreePool(MapListNode);
1095 } // for loop
1096 }
1097
1098 //
1099 // Find each handle with Simple File System
1100 //
1101 HandleList = GetHandleListByProtocol(&gEfiSimpleFileSystemProtocolGuid);
1102 if (HandleList != NULL) {
1103 //
1104 // Do a count of the handles
1105 //
1106 for (Count = 0 ; HandleList[Count] != NULL ; Count++);
1107
1108 //
1109 // Get all Device Paths
1110 //
1111 DevicePathList = AllocateZeroPool(sizeof(EFI_DEVICE_PATH_PROTOCOL*) * Count);
1112 ASSERT(DevicePathList != NULL);
1113
1114 for (Count = 0 ; HandleList[Count] != NULL ; Count++) {
1115 DevicePathList[Count] = DevicePathFromHandle(HandleList[Count]);
1116 }
1117
1118 //
1119 // Sort all DevicePaths
1120 //
1121 PerformQuickSort(DevicePathList, Count, sizeof(EFI_DEVICE_PATH_PROTOCOL*), DevicePathCompare);
1122
1123 ShellCommandConsistMappingInitialize(&ConsistMappingTable);
1124 //
1125 // Assign new Mappings to all...
1126 //
1127 for (Count = 0 ; HandleList[Count] != NULL ; Count++) {
1128 //
1129 // Get default name first
1130 //
1131 NewDefaultName = ShellCommandCreateNewMappingName(MappingTypeFileSystem);
1132 ASSERT(NewDefaultName != NULL);
1133 Status = ShellCommandAddMapItemAndUpdatePath(NewDefaultName, DevicePathList[Count], 0, TRUE);
1134 ASSERT_EFI_ERROR(Status);
1135 FreePool(NewDefaultName);
1136
1137 //
1138 // Now do consistent name
1139 //
1140 NewConsistName = ShellCommandConsistMappingGenMappingName(DevicePathList[Count], ConsistMappingTable);
1141 if (NewConsistName != NULL) {
1142 Status = ShellCommandAddMapItemAndUpdatePath(NewConsistName, DevicePathList[Count], 0, FALSE);
1143 ASSERT_EFI_ERROR(Status);
1144 FreePool(NewConsistName);
1145 }
1146 }
1147
1148 ShellCommandConsistMappingUnInitialize(ConsistMappingTable);
1149
1150 SHELL_FREE_NON_NULL(HandleList);
1151 SHELL_FREE_NON_NULL(DevicePathList);
1152
1153 HandleList = NULL;
1154 } else {
1155 Count = (UINTN)-1;
1156 }
1157
1158 //
1159 // Find each handle with Block Io
1160 //
1161 HandleList = GetHandleListByProtocol(&gEfiBlockIoProtocolGuid);
1162 if (HandleList != NULL) {
1163 for (Count = 0 ; HandleList[Count] != NULL ; Count++);
1164
1165 //
1166 // Get all Device Paths
1167 //
1168 DevicePathList = AllocateZeroPool(sizeof(EFI_DEVICE_PATH_PROTOCOL*) * Count);
1169 ASSERT(DevicePathList != NULL);
1170
1171 for (Count = 0 ; HandleList[Count] != NULL ; Count++) {
1172 DevicePathList[Count] = DevicePathFromHandle(HandleList[Count]);
1173 }
1174
1175 //
1176 // Sort all DevicePaths
1177 //
1178 PerformQuickSort(DevicePathList, Count, sizeof(EFI_DEVICE_PATH_PROTOCOL*), DevicePathCompare);
1179
1180 //
1181 // Assign new Mappings to all...
1182 //
1183 for (Count = 0 ; HandleList[Count] != NULL ; Count++) {
1184 //
1185 // Get default name first
1186 //
1187 NewDefaultName = ShellCommandCreateNewMappingName(MappingTypeBlockIo);
1188 ASSERT(NewDefaultName != NULL);
1189 Status = ShellCommandAddMapItemAndUpdatePath(NewDefaultName, DevicePathList[Count], 0, FALSE);
1190 ASSERT_EFI_ERROR(Status);
1191 FreePool(NewDefaultName);
1192 }
1193
1194 SHELL_FREE_NON_NULL(HandleList);
1195 SHELL_FREE_NON_NULL(DevicePathList);
1196 } else if (Count == (UINTN)-1) {
1197 return (EFI_NOT_FOUND);
1198 }
1199
1200 return (EFI_SUCCESS);
1201 }
1202
1203 /**
1204 Converts a SHELL_FILE_HANDLE to an EFI_FILE_PROTOCOL*.
1205
1206 @param[in] Handle The SHELL_FILE_HANDLE to convert.
1207
1208 @return a EFI_FILE_PROTOCOL* representing the same file.
1209 **/
1210 EFI_FILE_PROTOCOL*
1211 EFIAPI
1212 ConvertShellHandleToEfiFileProtocol(
1213 IN CONST SHELL_FILE_HANDLE Handle
1214 )
1215 {
1216 return ((EFI_FILE_PROTOCOL*)(Handle));
1217 }
1218
1219 /**
1220 Converts a EFI_FILE_PROTOCOL* to an SHELL_FILE_HANDLE.
1221
1222 @param[in] Handle The pointer to EFI_FILE_PROTOCOL to convert.
1223 @param[in] Path The path to the file for verification.
1224
1225 @return A SHELL_FILE_HANDLE representing the same file.
1226 @retval NULL There was not enough memory.
1227 **/
1228 SHELL_FILE_HANDLE
1229 EFIAPI
1230 ConvertEfiFileProtocolToShellHandle(
1231 IN CONST EFI_FILE_PROTOCOL *Handle,
1232 IN CONST CHAR16 *Path
1233 )
1234 {
1235 SHELL_COMMAND_FILE_HANDLE *Buffer;
1236 BUFFER_LIST *NewNode;
1237
1238 if (Path != NULL) {
1239 Buffer = AllocateZeroPool(sizeof(SHELL_COMMAND_FILE_HANDLE));
1240 if (Buffer == NULL) {
1241 return (NULL);
1242 }
1243 NewNode = AllocateZeroPool(sizeof(BUFFER_LIST));
1244 if (NewNode == NULL) {
1245 return (NULL);
1246 }
1247 Buffer->FileHandle = (EFI_FILE_PROTOCOL*)Handle;
1248 Buffer->Path = StrnCatGrow(&Buffer->Path, NULL, Path, 0);
1249 if (Buffer->Path == NULL) {
1250 return (NULL);
1251 }
1252 NewNode->Buffer = Buffer;
1253
1254 InsertHeadList(&mFileHandleList.Link, &NewNode->Link);
1255 }
1256 return ((SHELL_FILE_HANDLE)(Handle));
1257 }
1258
1259 /**
1260 Find the path that was logged with the specified SHELL_FILE_HANDLE.
1261
1262 @param[in] Handle The SHELL_FILE_HANDLE to query on.
1263
1264 @return A pointer to the path for the file.
1265 **/
1266 CONST CHAR16*
1267 EFIAPI
1268 ShellFileHandleGetPath(
1269 IN CONST SHELL_FILE_HANDLE Handle
1270 )
1271 {
1272 BUFFER_LIST *Node;
1273
1274 for (Node = (BUFFER_LIST*)GetFirstNode(&mFileHandleList.Link)
1275 ; !IsNull(&mFileHandleList.Link, &Node->Link)
1276 ; Node = (BUFFER_LIST*)GetNextNode(&mFileHandleList.Link, &Node->Link)
1277 ){
1278 if ((Node->Buffer) && (((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->FileHandle == Handle)){
1279 return (((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->Path);
1280 }
1281 }
1282 return (NULL);
1283 }
1284
1285 /**
1286 Remove a SHELL_FILE_HANDLE from the list of SHELL_FILE_HANDLES.
1287
1288 @param[in] Handle The SHELL_FILE_HANDLE to remove.
1289
1290 @retval TRUE The item was removed.
1291 @retval FALSE The item was not found.
1292 **/
1293 BOOLEAN
1294 EFIAPI
1295 ShellFileHandleRemove(
1296 IN CONST SHELL_FILE_HANDLE Handle
1297 )
1298 {
1299 BUFFER_LIST *Node;
1300
1301 for (Node = (BUFFER_LIST*)GetFirstNode(&mFileHandleList.Link)
1302 ; !IsNull(&mFileHandleList.Link, &Node->Link)
1303 ; Node = (BUFFER_LIST*)GetNextNode(&mFileHandleList.Link, &Node->Link)
1304 ){
1305 if ((Node->Buffer) && (((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->FileHandle == Handle)){
1306 RemoveEntryList(&Node->Link);
1307 SHELL_FREE_NON_NULL(((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->Path);
1308 SHELL_FREE_NON_NULL(Node->Buffer);
1309 SHELL_FREE_NON_NULL(Node);
1310 return (TRUE);
1311 }
1312 }
1313 return (FALSE);
1314 }
1315
1316 /**
1317 Function to determine if a SHELL_FILE_HANDLE is at the end of the file.
1318
1319 This will NOT work on directories.
1320
1321 If Handle is NULL, then ASSERT.
1322
1323 @param[in] Handle the file handle
1324
1325 @retval TRUE the position is at the end of the file
1326 @retval FALSE the position is not at the end of the file
1327 **/
1328 BOOLEAN
1329 EFIAPI
1330 ShellFileHandleEof(
1331 IN SHELL_FILE_HANDLE Handle
1332 )
1333 {
1334 EFI_FILE_INFO *Info;
1335 UINT64 Pos;
1336 BOOLEAN RetVal;
1337
1338 //
1339 // ASSERT if Handle is NULL
1340 //
1341 ASSERT(Handle != NULL);
1342
1343 gEfiShellProtocol->GetFilePosition(Handle, &Pos);
1344 Info = gEfiShellProtocol->GetFileInfo (Handle);
1345 ASSERT(Info != NULL);
1346 gEfiShellProtocol->SetFilePosition(Handle, Pos);
1347
1348 if (Info == NULL) {
1349 return (FALSE);
1350 }
1351
1352 if (Pos == Info->FileSize) {
1353 RetVal = TRUE;
1354 } else {
1355 RetVal = FALSE;
1356 }
1357
1358 FreePool (Info);
1359
1360 return (RetVal);
1361 }
1362
1363 /**
1364 Frees any BUFFER_LIST defined type.
1365
1366 @param[in] List The BUFFER_LIST object to free.
1367 **/
1368 VOID
1369 EFIAPI
1370 FreeBufferList (
1371 IN BUFFER_LIST *List
1372 )
1373 {
1374 BUFFER_LIST *BufferListEntry;
1375
1376 if (List == NULL){
1377 return;
1378 }
1379 //
1380 // enumerate through the buffer list and free all memory
1381 //
1382 for ( BufferListEntry = ( BUFFER_LIST *)GetFirstNode(&List->Link)
1383 ; !IsListEmpty (&List->Link)
1384 ; BufferListEntry = (BUFFER_LIST *)GetFirstNode(&List->Link)
1385 ){
1386 RemoveEntryList(&BufferListEntry->Link);
1387 ASSERT(BufferListEntry->Buffer != NULL);
1388 if (BufferListEntry->Buffer != NULL) {
1389 FreePool(BufferListEntry->Buffer);
1390 }
1391 FreePool(BufferListEntry);
1392 }
1393 }
1394