]> git.proxmox.com Git - mirror_edk2.git/blob - ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.c
ShellPkg/Dp: Add null pointer check
[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 - 2018, Intel Corporation. All rights reserved.<BR>
5 (C) Copyright 2013-2015 Hewlett-Packard Development Company, L.P.<BR>
6 (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
7
8 This program and the accompanying materials
9 are licensed and made available under the terms and conditions of the BSD License
10 which accompanies this distribution. The full text of the license may be found at
11 http://opensource.org/licenses/bsd-license.php
12
13 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
14 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15
16 **/
17
18 #include "UefiShellCommandLib.h"
19
20 // STATIC local variables
21 STATIC SHELL_COMMAND_INTERNAL_LIST_ENTRY mCommandList;
22 STATIC SCRIPT_FILE_LIST mScriptList;
23 STATIC ALIAS_LIST mAliasList;
24 STATIC BOOLEAN mEchoState;
25 STATIC BOOLEAN mExitRequested;
26 STATIC UINT64 mExitCode;
27 STATIC BOOLEAN mExitScript;
28 STATIC CHAR16 *mProfileList;
29 STATIC UINTN mProfileListSize;
30 STATIC UINTN mFsMaxCount = 0;
31 STATIC UINTN mBlkMaxCount = 0;
32 STATIC BUFFER_LIST mFileHandleList;
33
34 STATIC CONST CHAR8 Hex[] = {
35 '0',
36 '1',
37 '2',
38 '3',
39 '4',
40 '5',
41 '6',
42 '7',
43 '8',
44 '9',
45 'A',
46 'B',
47 'C',
48 'D',
49 'E',
50 'F'
51 };
52
53 // global variables required by library class.
54 EFI_UNICODE_COLLATION_PROTOCOL *gUnicodeCollation = NULL;
55 SHELL_MAP_LIST gShellMapList;
56 SHELL_MAP_LIST *gShellCurMapping = NULL;
57
58 CONST CHAR16* SupportLevel[] = {
59 L"Minimal",
60 L"Scripting",
61 L"Basic",
62 L"Interactive"
63 };
64
65 /**
66 Function to make sure that the global protocol pointers are valid.
67 must be called after constructor before accessing the pointers.
68 **/
69 EFI_STATUS
70 EFIAPI
71 CommandInit(
72 VOID
73 )
74 {
75 UINTN NumHandles;
76 EFI_HANDLE *Handles;
77 EFI_UNICODE_COLLATION_PROTOCOL *Uc;
78 CHAR8 *BestLanguage;
79 UINTN Index;
80 EFI_STATUS Status;
81 CHAR8 *PlatformLang;
82
83 GetEfiGlobalVariable2 (EFI_PLATFORM_LANG_VARIABLE_NAME, (VOID**)&PlatformLang, NULL);
84 if (PlatformLang == NULL) {
85 return EFI_UNSUPPORTED;
86 }
87
88 if (gUnicodeCollation == NULL) {
89 Status = gBS->LocateHandleBuffer (
90 ByProtocol,
91 &gEfiUnicodeCollation2ProtocolGuid,
92 NULL,
93 &NumHandles,
94 &Handles
95 );
96 if (EFI_ERROR (Status)) {
97 NumHandles = 0;
98 Handles = NULL;
99 }
100 for (Index = 0; Index < NumHandles; Index++) {
101 //
102 // Open Unicode Collation Protocol
103 //
104 Status = gBS->OpenProtocol (
105 Handles[Index],
106 &gEfiUnicodeCollation2ProtocolGuid,
107 (VOID **) &Uc,
108 gImageHandle,
109 NULL,
110 EFI_OPEN_PROTOCOL_GET_PROTOCOL
111 );
112 if (EFI_ERROR (Status)) {
113 continue;
114 }
115
116 //
117 // Find the best matching matching language from the supported languages
118 // of Unicode Collation2 protocol.
119 //
120 BestLanguage = GetBestLanguage (
121 Uc->SupportedLanguages,
122 FALSE,
123 PlatformLang,
124 NULL
125 );
126 if (BestLanguage != NULL) {
127 FreePool (BestLanguage);
128 gUnicodeCollation = Uc;
129 break;
130 }
131 }
132 if (Handles != NULL) {
133 FreePool (Handles);
134 }
135 FreePool (PlatformLang);
136 }
137
138 return (gUnicodeCollation == NULL) ? EFI_UNSUPPORTED : EFI_SUCCESS;
139 }
140
141 /**
142 Constructor for the Shell Command library.
143
144 Initialize the library and determine if the underlying is a UEFI Shell 2.0 or an EFI shell.
145
146 @param ImageHandle the image handle of the process
147 @param SystemTable the EFI System Table pointer
148
149 @retval EFI_SUCCESS the initialization was complete sucessfully
150 **/
151 RETURN_STATUS
152 EFIAPI
153 ShellCommandLibConstructor (
154 IN EFI_HANDLE ImageHandle,
155 IN EFI_SYSTEM_TABLE *SystemTable
156 )
157 {
158 EFI_STATUS Status;
159 InitializeListHead(&gShellMapList.Link);
160 InitializeListHead(&mCommandList.Link);
161 InitializeListHead(&mAliasList.Link);
162 InitializeListHead(&mScriptList.Link);
163 InitializeListHead(&mFileHandleList.Link);
164 mEchoState = TRUE;
165
166 mExitRequested = FALSE;
167 mExitScript = FALSE;
168 mProfileListSize = 0;
169 mProfileList = NULL;
170
171 Status = CommandInit ();
172 if (EFI_ERROR (Status)) {
173 return EFI_DEVICE_ERROR;
174 }
175
176 return (RETURN_SUCCESS);
177 }
178
179 /**
180 Frees list of file handles.
181
182 @param[in] List The list to free.
183 **/
184 VOID
185 FreeFileHandleList (
186 IN BUFFER_LIST *List
187 )
188 {
189 BUFFER_LIST *BufferListEntry;
190
191 if (List == NULL){
192 return;
193 }
194 //
195 // enumerate through the buffer list and free all memory
196 //
197 for ( BufferListEntry = ( BUFFER_LIST *)GetFirstNode(&List->Link)
198 ; !IsListEmpty (&List->Link)
199 ; BufferListEntry = (BUFFER_LIST *)GetFirstNode(&List->Link)
200 ){
201 RemoveEntryList(&BufferListEntry->Link);
202 ASSERT(BufferListEntry->Buffer != NULL);
203 SHELL_FREE_NON_NULL(((SHELL_COMMAND_FILE_HANDLE*)(BufferListEntry->Buffer))->Path);
204 SHELL_FREE_NON_NULL(BufferListEntry->Buffer);
205 SHELL_FREE_NON_NULL(BufferListEntry);
206 }
207 }
208
209 /**
210 Destructor for the library. free any resources.
211
212 @param ImageHandle the image handle of the process
213 @param SystemTable the EFI System Table pointer
214
215 @retval RETURN_SUCCESS this function always returns success
216 **/
217 RETURN_STATUS
218 EFIAPI
219 ShellCommandLibDestructor (
220 IN EFI_HANDLE ImageHandle,
221 IN EFI_SYSTEM_TABLE *SystemTable
222 )
223 {
224 SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
225 ALIAS_LIST *Node2;
226 SCRIPT_FILE_LIST *Node3;
227 SHELL_MAP_LIST *MapNode;
228 //
229 // enumerate throught the list and free all the memory
230 //
231 while (!IsListEmpty (&mCommandList.Link)) {
232 Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link);
233 RemoveEntryList(&Node->Link);
234 SHELL_FREE_NON_NULL(Node->CommandString);
235 FreePool(Node);
236 DEBUG_CODE(Node = NULL;);
237 }
238
239 //
240 // enumerate through the alias list and free all memory
241 //
242 while (!IsListEmpty (&mAliasList.Link)) {
243 Node2 = (ALIAS_LIST *)GetFirstNode(&mAliasList.Link);
244 RemoveEntryList(&Node2->Link);
245 SHELL_FREE_NON_NULL(Node2->CommandString);
246 SHELL_FREE_NON_NULL(Node2->Alias);
247 SHELL_FREE_NON_NULL(Node2);
248 DEBUG_CODE(Node2 = NULL;);
249 }
250
251 //
252 // enumerate throught the list and free all the memory
253 //
254 while (!IsListEmpty (&mScriptList.Link)) {
255 Node3 = (SCRIPT_FILE_LIST *)GetFirstNode(&mScriptList.Link);
256 RemoveEntryList(&Node3->Link);
257 DeleteScriptFileStruct(Node3->Data);
258 FreePool(Node3);
259 }
260
261 //
262 // enumerate throught the mappings list and free all the memory
263 //
264 if (!IsListEmpty(&gShellMapList.Link)) {
265 for (MapNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
266 ; !IsListEmpty (&gShellMapList.Link)
267 ; MapNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
268 ){
269 ASSERT(MapNode != NULL);
270 RemoveEntryList(&MapNode->Link);
271 SHELL_FREE_NON_NULL(MapNode->DevicePath);
272 SHELL_FREE_NON_NULL(MapNode->MapName);
273 SHELL_FREE_NON_NULL(MapNode->CurrentDirectoryPath);
274 FreePool(MapNode);
275 }
276 }
277 if (!IsListEmpty(&mFileHandleList.Link)){
278 FreeFileHandleList(&mFileHandleList);
279 }
280
281 if (mProfileList != NULL) {
282 FreePool(mProfileList);
283 }
284
285 gUnicodeCollation = NULL;
286 gShellCurMapping = NULL;
287
288 return (RETURN_SUCCESS);
289 }
290
291 /**
292 Find a dynamic command protocol instance given a command name string.
293
294 @param CommandString the command name string
295
296 @return instance the command protocol instance, if dynamic command instance found
297 @retval NULL no dynamic command protocol instance found for name
298 **/
299 CONST EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *
300 ShellCommandFindDynamicCommand (
301 IN CONST CHAR16 *CommandString
302 )
303 {
304 EFI_STATUS Status;
305 EFI_HANDLE *CommandHandleList;
306 EFI_HANDLE *NextCommand;
307 EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *DynamicCommand;
308
309 CommandHandleList = GetHandleListByProtocol(&gEfiShellDynamicCommandProtocolGuid);
310 if (CommandHandleList == NULL) {
311 //
312 // not found or out of resources
313 //
314 return NULL;
315 }
316
317 for (NextCommand = CommandHandleList; *NextCommand != NULL; NextCommand++) {
318 Status = gBS->HandleProtocol(
319 *NextCommand,
320 &gEfiShellDynamicCommandProtocolGuid,
321 (VOID **)&DynamicCommand
322 );
323
324 if (EFI_ERROR(Status)) {
325 continue;
326 }
327
328 if (gUnicodeCollation->StriColl(
329 gUnicodeCollation,
330 (CHAR16*)CommandString,
331 (CHAR16*)DynamicCommand->CommandName) == 0
332 ){
333 FreePool(CommandHandleList);
334 return (DynamicCommand);
335 }
336 }
337
338 FreePool(CommandHandleList);
339 return (NULL);
340 }
341
342 /**
343 Checks if a command exists as a dynamic command protocol instance
344
345 @param[in] CommandString The command string to check for on the list.
346 **/
347 BOOLEAN
348 ShellCommandDynamicCommandExists (
349 IN CONST CHAR16 *CommandString
350 )
351 {
352 return (BOOLEAN) ((ShellCommandFindDynamicCommand(CommandString) != NULL));
353 }
354
355 /**
356 Checks if a command is already on the internal command list.
357
358 @param[in] CommandString The command string to check for on the list.
359 **/
360 BOOLEAN
361 ShellCommandIsCommandOnInternalList(
362 IN CONST CHAR16 *CommandString
363 )
364 {
365 SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
366
367 //
368 // assert for NULL parameter
369 //
370 ASSERT(CommandString != NULL);
371
372 //
373 // check for the command
374 //
375 for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link)
376 ; !IsNull(&mCommandList.Link, &Node->Link)
377 ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode(&mCommandList.Link, &Node->Link)
378 ){
379 ASSERT(Node->CommandString != NULL);
380 if (gUnicodeCollation->StriColl(
381 gUnicodeCollation,
382 (CHAR16*)CommandString,
383 Node->CommandString) == 0
384 ){
385 return (TRUE);
386 }
387 }
388 return (FALSE);
389 }
390
391 /**
392 Checks if a command exists, either internally or through the dynamic command protocol.
393
394 @param[in] CommandString The command string to check for on the list.
395 **/
396 BOOLEAN
397 EFIAPI
398 ShellCommandIsCommandOnList(
399 IN CONST CHAR16 *CommandString
400 )
401 {
402 if (ShellCommandIsCommandOnInternalList(CommandString)) {
403 return TRUE;
404 }
405
406 return ShellCommandDynamicCommandExists(CommandString);
407 }
408
409 /**
410 Get the help text for a dynamic command.
411
412 @param[in] CommandString The command name.
413
414 @retval NULL No help text was found.
415 @return String of help text. Caller required to free.
416 **/
417 CHAR16*
418 ShellCommandGetDynamicCommandHelp(
419 IN CONST CHAR16 *CommandString
420 )
421 {
422 EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *DynamicCommand;
423
424 DynamicCommand = (EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *)ShellCommandFindDynamicCommand(CommandString);
425 if (DynamicCommand == NULL) {
426 return (NULL);
427 }
428
429 //
430 // TODO: how to get proper language?
431 //
432 return DynamicCommand->GetHelp(DynamicCommand, "en");
433 }
434
435 /**
436 Get the help text for an internal command.
437
438 @param[in] CommandString The command name.
439
440 @retval NULL No help text was found.
441 @return String of help text. Caller reuiqred to free.
442 **/
443 CHAR16*
444 ShellCommandGetInternalCommandHelp(
445 IN CONST CHAR16 *CommandString
446 )
447 {
448 SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
449
450 //
451 // assert for NULL parameter
452 //
453 ASSERT(CommandString != NULL);
454
455 //
456 // check for the command
457 //
458 for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link)
459 ; !IsNull(&mCommandList.Link, &Node->Link)
460 ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode(&mCommandList.Link, &Node->Link)
461 ){
462 ASSERT(Node->CommandString != NULL);
463 if (gUnicodeCollation->StriColl(
464 gUnicodeCollation,
465 (CHAR16*)CommandString,
466 Node->CommandString) == 0
467 ){
468 return (HiiGetString(Node->HiiHandle, Node->ManFormatHelp, NULL));
469 }
470 }
471 return (NULL);
472 }
473
474 /**
475 Get the help text for a command.
476
477 @param[in] CommandString The command name.
478
479 @retval NULL No help text was found.
480 @return String of help text.Caller reuiqred to free.
481 **/
482 CHAR16*
483 EFIAPI
484 ShellCommandGetCommandHelp (
485 IN CONST CHAR16 *CommandString
486 )
487 {
488 CHAR16 *HelpStr;
489 HelpStr = ShellCommandGetInternalCommandHelp(CommandString);
490
491 if (HelpStr == NULL) {
492 HelpStr = ShellCommandGetDynamicCommandHelp(CommandString);
493 }
494
495 return HelpStr;
496 }
497
498
499 /**
500 Registers handlers of type SHELL_RUN_COMMAND and
501 SHELL_GET_MAN_FILENAME for each shell command.
502
503 If the ShellSupportLevel is greater than the value of the
504 PcdShellSupportLevel then return RETURN_UNSUPPORTED.
505
506 Registers the handlers specified by GetHelpInfoHandler and CommandHandler
507 with the command specified by CommandString. If the command named by
508 CommandString has already been registered, then return
509 RETURN_ALREADY_STARTED.
510
511 If there are not enough resources available to register the handlers then
512 RETURN_OUT_OF_RESOURCES is returned.
513
514 If CommandString is NULL, then ASSERT().
515 If GetHelpInfoHandler is NULL, then ASSERT().
516 If CommandHandler is NULL, then ASSERT().
517 If ProfileName is NULL, then ASSERT().
518
519 @param[in] CommandString Pointer to the command name. This is the
520 name to look for on the command line in
521 the shell.
522 @param[in] CommandHandler Pointer to a function that runs the
523 specified command.
524 @param[in] GetManFileName Pointer to a function that provides man
525 filename.
526 @param[in] ShellMinSupportLevel minimum Shell Support Level which has this
527 function.
528 @param[in] ProfileName profile name to require for support of this
529 function.
530 @param[in] CanAffectLE indicates whether this command's return value
531 can change the LASTERROR environment variable.
532 @param[in] HiiHandle Handle of this command's HII entry.
533 @param[in] ManFormatHelp HII locator for the help text.
534
535 @retval RETURN_SUCCESS The handlers were registered.
536 @retval RETURN_OUT_OF_RESOURCES There are not enough resources available to
537 register the shell command.
538 @retval RETURN_UNSUPPORTED the ShellMinSupportLevel was higher than the
539 currently allowed support level.
540 @retval RETURN_ALREADY_STARTED The CommandString represents a command that
541 is already registered. Only 1 handler set for
542 a given command is allowed.
543 @sa SHELL_GET_MAN_FILENAME
544 @sa SHELL_RUN_COMMAND
545 **/
546 RETURN_STATUS
547 EFIAPI
548 ShellCommandRegisterCommandName (
549 IN CONST CHAR16 *CommandString,
550 IN SHELL_RUN_COMMAND CommandHandler,
551 IN SHELL_GET_MAN_FILENAME GetManFileName,
552 IN UINT32 ShellMinSupportLevel,
553 IN CONST CHAR16 *ProfileName,
554 IN CONST BOOLEAN CanAffectLE,
555 IN CONST EFI_HANDLE HiiHandle,
556 IN CONST EFI_STRING_ID ManFormatHelp
557 )
558 {
559 SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
560 SHELL_COMMAND_INTERNAL_LIST_ENTRY *Command;
561 SHELL_COMMAND_INTERNAL_LIST_ENTRY *PrevCommand;
562 INTN LexicalMatchValue;
563
564 //
565 // Initialize local variables.
566 //
567 Command = NULL;
568 PrevCommand = NULL;
569 LexicalMatchValue = 0;
570
571 //
572 // ASSERTs for NULL parameters
573 //
574 ASSERT(CommandString != NULL);
575 ASSERT(GetManFileName != NULL);
576 ASSERT(CommandHandler != NULL);
577 ASSERT(ProfileName != NULL);
578
579 //
580 // check for shell support level
581 //
582 if (PcdGet8(PcdShellSupportLevel) < ShellMinSupportLevel) {
583 return (RETURN_UNSUPPORTED);
584 }
585
586 //
587 // check for already on the list
588 //
589 if (ShellCommandIsCommandOnList(CommandString)) {
590 return (RETURN_ALREADY_STARTED);
591 }
592
593 //
594 // allocate memory for new struct
595 //
596 Node = AllocateZeroPool(sizeof(SHELL_COMMAND_INTERNAL_LIST_ENTRY));
597 if (Node == NULL) {
598 return RETURN_OUT_OF_RESOURCES;
599 }
600 Node->CommandString = AllocateCopyPool(StrSize(CommandString), CommandString);
601 if (Node->CommandString == NULL) {
602 FreePool (Node);
603 return RETURN_OUT_OF_RESOURCES;
604 }
605
606 Node->GetManFileName = GetManFileName;
607 Node->CommandHandler = CommandHandler;
608 Node->LastError = CanAffectLE;
609 Node->HiiHandle = HiiHandle;
610 Node->ManFormatHelp = ManFormatHelp;
611
612 if ( StrLen(ProfileName)>0
613 && ((mProfileList != NULL
614 && StrStr(mProfileList, ProfileName) == NULL) || mProfileList == NULL)
615 ){
616 ASSERT((mProfileList == NULL && mProfileListSize == 0) || (mProfileList != NULL));
617 if (mProfileList == NULL) {
618 //
619 // If this is the first make a leading ';'
620 //
621 StrnCatGrow(&mProfileList, &mProfileListSize, L";", 0);
622 }
623 StrnCatGrow(&mProfileList, &mProfileListSize, ProfileName, 0);
624 StrnCatGrow(&mProfileList, &mProfileListSize, L";", 0);
625 }
626
627 //
628 // Insert a new entry on top of the list
629 //
630 InsertHeadList (&mCommandList.Link, &Node->Link);
631
632 //
633 // Move a new registered command to its sorted ordered location in the list
634 //
635 for (Command = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode (&mCommandList.Link),
636 PrevCommand = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode (&mCommandList.Link)
637 ; !IsNull (&mCommandList.Link, &Command->Link)
638 ; Command = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode (&mCommandList.Link, &Command->Link)) {
639
640 //
641 // Get Lexical Comparison Value between PrevCommand and Command list entry
642 //
643 LexicalMatchValue = gUnicodeCollation->StriColl (
644 gUnicodeCollation,
645 PrevCommand->CommandString,
646 Command->CommandString
647 );
648
649 //
650 // Swap PrevCommand and Command list entry if PrevCommand list entry
651 // is alphabetically greater than Command list entry
652 //
653 if (LexicalMatchValue > 0){
654 Command = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *) SwapListEntries (&PrevCommand->Link, &Command->Link);
655 } else if (LexicalMatchValue < 0) {
656 //
657 // PrevCommand entry is lexically lower than Command entry
658 //
659 break;
660 }
661 }
662
663 return (RETURN_SUCCESS);
664 }
665
666 /**
667 Function to get the current Profile string.
668
669 @retval NULL There are no installed profiles.
670 @return A semi-colon delimited list of profiles.
671 **/
672 CONST CHAR16 *
673 EFIAPI
674 ShellCommandGetProfileList (
675 VOID
676 )
677 {
678 return (mProfileList);
679 }
680
681 /**
682 Checks if a command string has been registered for CommandString and if so it runs
683 the previously registered handler for that command with the command line.
684
685 If CommandString is NULL, then ASSERT().
686
687 If Sections is specified, then each section name listed will be compared in a casesensitive
688 manner, to the section names described in Appendix B UEFI Shell 2.0 spec. If the section exists,
689 it will be appended to the returned help text. If the section does not exist, no
690 information will be returned. If Sections is NULL, then all help text information
691 available will be returned.
692
693 @param[in] CommandString Pointer to the command name. This is the name
694 found on the command line in the shell.
695 @param[in, out] RetVal Pointer to the return vaule from the command handler.
696
697 @param[in, out] CanAffectLE indicates whether this command's return value
698 needs to be placed into LASTERROR environment variable.
699
700 @retval RETURN_SUCCESS The handler was run.
701 @retval RETURN_NOT_FOUND The CommandString did not match a registered
702 command name.
703 @sa SHELL_RUN_COMMAND
704 **/
705 RETURN_STATUS
706 EFIAPI
707 ShellCommandRunCommandHandler (
708 IN CONST CHAR16 *CommandString,
709 IN OUT SHELL_STATUS *RetVal,
710 IN OUT BOOLEAN *CanAffectLE OPTIONAL
711 )
712 {
713 SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
714 EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *DynamicCommand;
715
716 //
717 // assert for NULL parameters
718 //
719 ASSERT(CommandString != NULL);
720
721 //
722 // check for the command
723 //
724 for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link)
725 ; !IsNull(&mCommandList.Link, &Node->Link)
726 ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode(&mCommandList.Link, &Node->Link)
727 ){
728 ASSERT(Node->CommandString != NULL);
729 if (gUnicodeCollation->StriColl(
730 gUnicodeCollation,
731 (CHAR16*)CommandString,
732 Node->CommandString) == 0
733 ){
734 if (CanAffectLE != NULL) {
735 *CanAffectLE = Node->LastError;
736 }
737 if (RetVal != NULL) {
738 *RetVal = Node->CommandHandler(NULL, gST);
739 } else {
740 Node->CommandHandler(NULL, gST);
741 }
742 return (RETURN_SUCCESS);
743 }
744 }
745
746 //
747 // An internal command was not found, try to find a dynamic command
748 //
749 DynamicCommand = (EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *)ShellCommandFindDynamicCommand(CommandString);
750 if (DynamicCommand != NULL) {
751 if (RetVal != NULL) {
752 *RetVal = DynamicCommand->Handler(DynamicCommand, gST, gEfiShellParametersProtocol, gEfiShellProtocol);
753 } else {
754 DynamicCommand->Handler(DynamicCommand, gST, gEfiShellParametersProtocol, gEfiShellProtocol);
755 }
756 return (RETURN_SUCCESS);
757 }
758
759 return (RETURN_NOT_FOUND);
760 }
761
762 /**
763 Checks if a command string has been registered for CommandString and if so it
764 returns the MAN filename specified for that command.
765
766 If CommandString is NULL, then ASSERT().
767
768 @param[in] CommandString Pointer to the command name. This is the name
769 found on the command line in the shell.\
770
771 @retval NULL the commandString was not a registered command.
772 @return other the name of the MAN file.
773 @sa SHELL_GET_MAN_FILENAME
774 **/
775 CONST CHAR16*
776 EFIAPI
777 ShellCommandGetManFileNameHandler (
778 IN CONST CHAR16 *CommandString
779 )
780 {
781 SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
782
783 //
784 // assert for NULL parameters
785 //
786 ASSERT(CommandString != NULL);
787
788 //
789 // check for the command
790 //
791 for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link)
792 ; !IsNull(&mCommandList.Link, &Node->Link)
793 ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode(&mCommandList.Link, &Node->Link)
794 ){
795 ASSERT(Node->CommandString != NULL);
796 if (gUnicodeCollation->StriColl(
797 gUnicodeCollation,
798 (CHAR16*)CommandString,
799 Node->CommandString) == 0
800 ){
801 return (Node->GetManFileName());
802 }
803 }
804 return (NULL);
805 }
806
807 /**
808 Get the list of all available shell internal commands. This is a linked list
809 (via LIST_ENTRY structure). enumerate through it using the BaseLib linked
810 list functions. do not modify the values.
811
812 @param[in] Sort TRUE to alphabetically sort the values first. FALSE otherwise.
813
814 @return a Linked list of all available shell commands.
815 **/
816 CONST COMMAND_LIST*
817 EFIAPI
818 ShellCommandGetCommandList (
819 IN CONST BOOLEAN Sort
820 )
821 {
822 // if (!Sort) {
823 // return ((COMMAND_LIST*)(&mCommandList));
824 // }
825 return ((COMMAND_LIST*)(&mCommandList));
826 }
827
828 /**
829 Registers aliases to be set as part of the initialization of the shell application.
830
831 If Command is NULL, then ASSERT().
832 If Alias is NULL, then ASSERT().
833
834 @param[in] Command Pointer to the Command
835 @param[in] Alias Pointer to Alias
836
837 @retval RETURN_SUCCESS The handlers were registered.
838 @retval RETURN_OUT_OF_RESOURCES There are not enough resources available to
839 register the shell command.
840 **/
841 RETURN_STATUS
842 EFIAPI
843 ShellCommandRegisterAlias (
844 IN CONST CHAR16 *Command,
845 IN CONST CHAR16 *Alias
846 )
847 {
848 ALIAS_LIST *Node;
849 ALIAS_LIST *CommandAlias;
850 ALIAS_LIST *PrevCommandAlias;
851 INTN LexicalMatchValue;
852
853 //
854 // Asserts for NULL
855 //
856 ASSERT(Command != NULL);
857 ASSERT(Alias != NULL);
858
859 //
860 // allocate memory for new struct
861 //
862 Node = AllocateZeroPool(sizeof(ALIAS_LIST));
863 if (Node == NULL) {
864 return RETURN_OUT_OF_RESOURCES;
865 }
866 Node->CommandString = AllocateCopyPool(StrSize(Command), Command);
867 if (Node->CommandString == NULL) {
868 FreePool (Node);
869 return RETURN_OUT_OF_RESOURCES;
870 }
871 Node->Alias = AllocateCopyPool(StrSize(Alias), Alias);
872 if (Node->Alias == NULL) {
873 FreePool (Node->CommandString);
874 FreePool (Node);
875 return RETURN_OUT_OF_RESOURCES;
876 }
877
878 InsertHeadList (&mAliasList.Link, &Node->Link);
879
880 //
881 // Move a new pre-defined registered alias to its sorted ordered location in the list
882 //
883 for ( CommandAlias = (ALIAS_LIST *)GetFirstNode (&mAliasList.Link),
884 PrevCommandAlias = (ALIAS_LIST *)GetFirstNode (&mAliasList.Link)
885 ; !IsNull (&mAliasList.Link, &CommandAlias->Link)
886 ; CommandAlias = (ALIAS_LIST *) GetNextNode (&mAliasList.Link, &CommandAlias->Link) ) {
887 //
888 // Get Lexical comparison value between PrevCommandAlias and CommandAlias List Entry
889 //
890 LexicalMatchValue = gUnicodeCollation->StriColl (
891 gUnicodeCollation,
892 PrevCommandAlias->Alias,
893 CommandAlias->Alias
894 );
895
896 //
897 // Swap PrevCommandAlias and CommandAlias list entry if PrevCommandAlias list entry
898 // is alphabetically greater than CommandAlias list entry
899 //
900 if (LexicalMatchValue > 0) {
901 CommandAlias = (ALIAS_LIST *) SwapListEntries (&PrevCommandAlias->Link, &CommandAlias->Link);
902 } else if (LexicalMatchValue < 0) {
903 //
904 // PrevCommandAlias entry is lexically lower than CommandAlias entry
905 //
906 break;
907 }
908 }
909
910 return (RETURN_SUCCESS);
911 }
912
913 /**
914 Get the list of all shell alias commands. This is a linked list
915 (via LIST_ENTRY structure). enumerate through it using the BaseLib linked
916 list functions. do not modify the values.
917
918 @return a Linked list of all requested shell alias'.
919 **/
920 CONST ALIAS_LIST*
921 EFIAPI
922 ShellCommandGetInitAliasList (
923 VOID
924 )
925 {
926 return (&mAliasList);
927 }
928
929 /**
930 Determine if a given alias is on the list of built in alias'.
931
932 @param[in] Alias The alias to test for
933
934 @retval TRUE The alias is a built in alias
935 @retval FALSE The alias is not a built in alias
936 **/
937 BOOLEAN
938 EFIAPI
939 ShellCommandIsOnAliasList(
940 IN CONST CHAR16 *Alias
941 )
942 {
943 ALIAS_LIST *Node;
944
945 //
946 // assert for NULL parameter
947 //
948 ASSERT(Alias != NULL);
949
950 //
951 // check for the Alias
952 //
953 for ( Node = (ALIAS_LIST *)GetFirstNode(&mAliasList.Link)
954 ; !IsNull(&mAliasList.Link, &Node->Link)
955 ; Node = (ALIAS_LIST *)GetNextNode(&mAliasList.Link, &Node->Link)
956 ){
957 ASSERT(Node->CommandString != NULL);
958 ASSERT(Node->Alias != NULL);
959 if (gUnicodeCollation->StriColl(
960 gUnicodeCollation,
961 (CHAR16*)Alias,
962 Node->CommandString) == 0
963 ){
964 return (TRUE);
965 }
966 if (gUnicodeCollation->StriColl(
967 gUnicodeCollation,
968 (CHAR16*)Alias,
969 Node->Alias) == 0
970 ){
971 return (TRUE);
972 }
973 }
974 return (FALSE);
975 }
976
977 /**
978 Function to determine current state of ECHO. Echo determines if lines from scripts
979 and ECHO commands are enabled.
980
981 @retval TRUE Echo is currently enabled
982 @retval FALSE Echo is currently disabled
983 **/
984 BOOLEAN
985 EFIAPI
986 ShellCommandGetEchoState(
987 VOID
988 )
989 {
990 return (mEchoState);
991 }
992
993 /**
994 Function to set current state of ECHO. Echo determines if lines from scripts
995 and ECHO commands are enabled.
996
997 If State is TRUE, Echo will be enabled.
998 If State is FALSE, Echo will be disabled.
999
1000 @param[in] State How to set echo.
1001 **/
1002 VOID
1003 EFIAPI
1004 ShellCommandSetEchoState(
1005 IN BOOLEAN State
1006 )
1007 {
1008 mEchoState = State;
1009 }
1010
1011 /**
1012 Indicate that the current shell or script should exit.
1013
1014 @param[in] ScriptOnly TRUE if exiting a script; FALSE otherwise.
1015 @param[in] ErrorCode The 64 bit error code to return.
1016 **/
1017 VOID
1018 EFIAPI
1019 ShellCommandRegisterExit (
1020 IN BOOLEAN ScriptOnly,
1021 IN CONST UINT64 ErrorCode
1022 )
1023 {
1024 mExitRequested = (BOOLEAN)(!mExitRequested);
1025 if (mExitRequested) {
1026 mExitScript = ScriptOnly;
1027 } else {
1028 mExitScript = FALSE;
1029 }
1030 mExitCode = ErrorCode;
1031 }
1032
1033 /**
1034 Retrieve the Exit indicator.
1035
1036 @retval TRUE Exit was indicated.
1037 @retval FALSE Exis was not indicated.
1038 **/
1039 BOOLEAN
1040 EFIAPI
1041 ShellCommandGetExit (
1042 VOID
1043 )
1044 {
1045 return (mExitRequested);
1046 }
1047
1048 /**
1049 Retrieve the Exit code.
1050
1051 If ShellCommandGetExit returns FALSE than the return from this is undefined.
1052
1053 @return the value passed into RegisterExit.
1054 **/
1055 UINT64
1056 EFIAPI
1057 ShellCommandGetExitCode (
1058 VOID
1059 )
1060 {
1061 return (mExitCode);
1062 }
1063 /**
1064 Retrieve the Exit script indicator.
1065
1066 If ShellCommandGetExit returns FALSE than the return from this is undefined.
1067
1068 @retval TRUE ScriptOnly was indicated.
1069 @retval FALSE ScriptOnly was not indicated.
1070 **/
1071 BOOLEAN
1072 EFIAPI
1073 ShellCommandGetScriptExit (
1074 VOID
1075 )
1076 {
1077 return (mExitScript);
1078 }
1079
1080 /**
1081 Function to cleanup all memory from a SCRIPT_FILE structure.
1082
1083 @param[in] Script The pointer to the structure to cleanup.
1084 **/
1085 VOID
1086 EFIAPI
1087 DeleteScriptFileStruct (
1088 IN SCRIPT_FILE *Script
1089 )
1090 {
1091 UINT8 LoopVar;
1092
1093 if (Script == NULL) {
1094 return;
1095 }
1096
1097 for (LoopVar = 0 ; LoopVar < Script->Argc ; LoopVar++) {
1098 SHELL_FREE_NON_NULL(Script->Argv[LoopVar]);
1099 }
1100 if (Script->Argv != NULL) {
1101 SHELL_FREE_NON_NULL(Script->Argv);
1102 }
1103 Script->CurrentCommand = NULL;
1104 while (!IsListEmpty (&Script->CommandList)) {
1105 Script->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetFirstNode(&Script->CommandList);
1106 if (Script->CurrentCommand != NULL) {
1107 RemoveEntryList(&Script->CurrentCommand->Link);
1108 if (Script->CurrentCommand->Cl != NULL) {
1109 SHELL_FREE_NON_NULL(Script->CurrentCommand->Cl);
1110 }
1111 if (Script->CurrentCommand->Data != NULL) {
1112 SHELL_FREE_NON_NULL(Script->CurrentCommand->Data);
1113 }
1114 SHELL_FREE_NON_NULL(Script->CurrentCommand);
1115 }
1116 }
1117 SHELL_FREE_NON_NULL(Script->ScriptName);
1118 SHELL_FREE_NON_NULL(Script);
1119 }
1120
1121 /**
1122 Function to return a pointer to the currently running script file object.
1123
1124 @retval NULL A script file is not currently running.
1125 @return A pointer to the current script file object.
1126 **/
1127 SCRIPT_FILE*
1128 EFIAPI
1129 ShellCommandGetCurrentScriptFile (
1130 VOID
1131 )
1132 {
1133 SCRIPT_FILE_LIST *List;
1134 if (IsListEmpty (&mScriptList.Link)) {
1135 return (NULL);
1136 }
1137 List = ((SCRIPT_FILE_LIST*)GetFirstNode(&mScriptList.Link));
1138 return (List->Data);
1139 }
1140
1141 /**
1142 Function to set a new script as the currently running one.
1143
1144 This function will correctly stack and unstack nested scripts.
1145
1146 @param[in] Script Pointer to new script information structure. if NULL
1147 will remove and de-allocate the top-most Script structure.
1148
1149 @return A pointer to the current running script file after this
1150 change. NULL if removing the final script.
1151 **/
1152 SCRIPT_FILE*
1153 EFIAPI
1154 ShellCommandSetNewScript (
1155 IN SCRIPT_FILE *Script OPTIONAL
1156 )
1157 {
1158 SCRIPT_FILE_LIST *Node;
1159 if (Script == NULL) {
1160 if (IsListEmpty (&mScriptList.Link)) {
1161 return (NULL);
1162 }
1163 Node = (SCRIPT_FILE_LIST *)GetFirstNode(&mScriptList.Link);
1164 RemoveEntryList(&Node->Link);
1165 DeleteScriptFileStruct(Node->Data);
1166 FreePool(Node);
1167 } else {
1168 Node = AllocateZeroPool(sizeof(SCRIPT_FILE_LIST));
1169 if (Node == NULL) {
1170 return (NULL);
1171 }
1172 Node->Data = Script;
1173 InsertHeadList(&mScriptList.Link, &Node->Link);
1174 }
1175 return (ShellCommandGetCurrentScriptFile());
1176 }
1177
1178 /**
1179 Function to generate the next default mapping name.
1180
1181 If the return value is not NULL then it must be callee freed.
1182
1183 @param Type What kind of mapping name to make.
1184
1185 @retval NULL a memory allocation failed.
1186 @return a new map name string
1187 **/
1188 CHAR16*
1189 EFIAPI
1190 ShellCommandCreateNewMappingName(
1191 IN CONST SHELL_MAPPING_TYPE Type
1192 )
1193 {
1194 CHAR16 *String;
1195 ASSERT(Type < MappingTypeMax);
1196
1197 String = NULL;
1198
1199 String = AllocateZeroPool(PcdGet8(PcdShellMapNameLength) * sizeof(String[0]));
1200 UnicodeSPrint(
1201 String,
1202 PcdGet8(PcdShellMapNameLength) * sizeof(String[0]),
1203 Type == MappingTypeFileSystem?L"FS%d:":L"BLK%d:",
1204 Type == MappingTypeFileSystem?mFsMaxCount++:mBlkMaxCount++);
1205
1206 return (String);
1207 }
1208
1209 /**
1210 Function to add a map node to the list of map items and update the "path" environment variable (optionally).
1211
1212 If Path is TRUE (during initialization only), the path environment variable will also be updated to include
1213 default paths on the new map name...
1214
1215 Path should be FALSE when this function is called from the protocol SetMap function.
1216
1217 @param[in] Name The human readable mapped name.
1218 @param[in] DevicePath The Device Path for this map.
1219 @param[in] Flags The Flags attribute for this map item.
1220 @param[in] Path TRUE to update path, FALSE to skip this step (should only be TRUE during initialization).
1221
1222 @retval EFI_SUCCESS The addition was sucessful.
1223 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
1224 @retval EFI_INVALID_PARAMETER A parameter was invalid.
1225 **/
1226 EFI_STATUS
1227 EFIAPI
1228 ShellCommandAddMapItemAndUpdatePath(
1229 IN CONST CHAR16 *Name,
1230 IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath,
1231 IN CONST UINT64 Flags,
1232 IN CONST BOOLEAN Path
1233 )
1234 {
1235 EFI_STATUS Status;
1236 SHELL_MAP_LIST *MapListNode;
1237 CONST CHAR16 *OriginalPath;
1238 CHAR16 *NewPath;
1239 UINTN NewPathSize;
1240
1241 NewPathSize = 0;
1242 NewPath = NULL;
1243 OriginalPath = NULL;
1244 Status = EFI_SUCCESS;
1245
1246 MapListNode = AllocateZeroPool(sizeof(SHELL_MAP_LIST));
1247 if (MapListNode == NULL) {
1248 Status = EFI_OUT_OF_RESOURCES;
1249 } else {
1250 MapListNode->Flags = Flags;
1251 MapListNode->MapName = AllocateCopyPool(StrSize(Name), Name);
1252 MapListNode->DevicePath = DuplicateDevicePath(DevicePath);
1253 if ((MapListNode->MapName == NULL) || (MapListNode->DevicePath == NULL)){
1254 Status = EFI_OUT_OF_RESOURCES;
1255 } else {
1256 InsertTailList(&gShellMapList.Link, &MapListNode->Link);
1257 }
1258 }
1259 if (EFI_ERROR(Status)) {
1260 if (MapListNode != NULL) {
1261 if (MapListNode->DevicePath != NULL) {
1262 FreePool(MapListNode->DevicePath);
1263 }
1264 if (MapListNode->MapName != NULL) {
1265 FreePool(MapListNode->MapName);
1266 }
1267 FreePool(MapListNode);
1268 }
1269 } else if (Path) {
1270 //
1271 // Since there was no error and Path was TRUE
1272 // Now add the correct path for that mapping
1273 //
1274 OriginalPath = gEfiShellProtocol->GetEnv(L"path");
1275 ASSERT((NewPath == NULL && NewPathSize == 0) || (NewPath != NULL));
1276 if (OriginalPath != NULL) {
1277 StrnCatGrow(&NewPath, &NewPathSize, OriginalPath, 0);
1278 StrnCatGrow(&NewPath, &NewPathSize, L";", 0);
1279 }
1280 StrnCatGrow(&NewPath, &NewPathSize, Name, 0);
1281 StrnCatGrow(&NewPath, &NewPathSize, L"\\efi\\tools\\;", 0);
1282 StrnCatGrow(&NewPath, &NewPathSize, Name, 0);
1283 StrnCatGrow(&NewPath, &NewPathSize, L"\\efi\\boot\\;", 0);
1284 StrnCatGrow(&NewPath, &NewPathSize, Name, 0);
1285 StrnCatGrow(&NewPath, &NewPathSize, L"\\", 0);
1286
1287 Status = gEfiShellProtocol->SetEnv(L"path", NewPath, TRUE);
1288 ASSERT_EFI_ERROR(Status);
1289 FreePool(NewPath);
1290 }
1291 return (Status);
1292 }
1293
1294 /**
1295 Creates the default map names for each device path in the system with
1296 a protocol depending on the Type.
1297
1298 Creates the consistent map names for each device path in the system with
1299 a protocol depending on the Type.
1300
1301 Note: This will reset all mappings in the system("map -r").
1302
1303 Also sets up the default path environment variable if Type is FileSystem.
1304
1305 @retval EFI_SUCCESS All map names were created sucessfully.
1306 @retval EFI_NOT_FOUND No protocols were found in the system.
1307 @return Error returned from gBS->LocateHandle().
1308
1309 @sa LocateHandle
1310 **/
1311 EFI_STATUS
1312 EFIAPI
1313 ShellCommandCreateInitialMappingsAndPaths(
1314 VOID
1315 )
1316 {
1317 EFI_STATUS Status;
1318 EFI_HANDLE *HandleList;
1319 UINTN Count;
1320 EFI_DEVICE_PATH_PROTOCOL **DevicePathList;
1321 CHAR16 *NewDefaultName;
1322 CHAR16 *NewConsistName;
1323 EFI_DEVICE_PATH_PROTOCOL **ConsistMappingTable;
1324 SHELL_MAP_LIST *MapListNode;
1325 CONST CHAR16 *CurDir;
1326 CHAR16 *SplitCurDir;
1327 CHAR16 *MapName;
1328 SHELL_MAP_LIST *MapListItem;
1329
1330 SplitCurDir = NULL;
1331 MapName = NULL;
1332 MapListItem = NULL;
1333 HandleList = NULL;
1334
1335 //
1336 // Reset the static members back to zero
1337 //
1338 mFsMaxCount = 0;
1339 mBlkMaxCount = 0;
1340
1341 gEfiShellProtocol->SetEnv(L"path", L"", TRUE);
1342
1343 //
1344 // First empty out the existing list.
1345 //
1346 if (!IsListEmpty(&gShellMapList.Link)) {
1347 for ( MapListNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
1348 ; !IsListEmpty(&gShellMapList.Link)
1349 ; MapListNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
1350 ){
1351 RemoveEntryList(&MapListNode->Link);
1352 SHELL_FREE_NON_NULL(MapListNode->DevicePath);
1353 SHELL_FREE_NON_NULL(MapListNode->MapName);
1354 SHELL_FREE_NON_NULL(MapListNode->CurrentDirectoryPath);
1355 FreePool(MapListNode);
1356 } // for loop
1357 }
1358
1359 //
1360 // Find each handle with Simple File System
1361 //
1362 HandleList = GetHandleListByProtocol(&gEfiSimpleFileSystemProtocolGuid);
1363 if (HandleList != NULL) {
1364 //
1365 // Do a count of the handles
1366 //
1367 for (Count = 0 ; HandleList[Count] != NULL ; Count++);
1368
1369 //
1370 // Get all Device Paths
1371 //
1372 DevicePathList = AllocateZeroPool(sizeof(EFI_DEVICE_PATH_PROTOCOL*) * Count);
1373 if (DevicePathList == NULL) {
1374 SHELL_FREE_NON_NULL (HandleList);
1375 return EFI_OUT_OF_RESOURCES;
1376 }
1377
1378 for (Count = 0 ; HandleList[Count] != NULL ; Count++) {
1379 DevicePathList[Count] = DevicePathFromHandle(HandleList[Count]);
1380 }
1381
1382 //
1383 // Sort all DevicePaths
1384 //
1385 PerformQuickSort(DevicePathList, Count, sizeof(EFI_DEVICE_PATH_PROTOCOL*), DevicePathCompare);
1386
1387 ShellCommandConsistMappingInitialize(&ConsistMappingTable);
1388 //
1389 // Assign new Mappings to all...
1390 //
1391 for (Count = 0 ; HandleList[Count] != NULL ; Count++) {
1392 //
1393 // Get default name first
1394 //
1395 NewDefaultName = ShellCommandCreateNewMappingName(MappingTypeFileSystem);
1396 ASSERT(NewDefaultName != NULL);
1397 Status = ShellCommandAddMapItemAndUpdatePath(NewDefaultName, DevicePathList[Count], 0, TRUE);
1398 ASSERT_EFI_ERROR(Status);
1399 FreePool(NewDefaultName);
1400
1401 //
1402 // Now do consistent name
1403 //
1404 NewConsistName = ShellCommandConsistMappingGenMappingName(DevicePathList[Count], ConsistMappingTable);
1405 if (NewConsistName != NULL) {
1406 Status = ShellCommandAddMapItemAndUpdatePath(NewConsistName, DevicePathList[Count], 0, FALSE);
1407 ASSERT_EFI_ERROR(Status);
1408 FreePool(NewConsistName);
1409 }
1410 }
1411
1412 ShellCommandConsistMappingUnInitialize(ConsistMappingTable);
1413
1414 SHELL_FREE_NON_NULL(HandleList);
1415 SHELL_FREE_NON_NULL(DevicePathList);
1416
1417 HandleList = NULL;
1418
1419 //
1420 //gShellCurMapping point to node of current file system in the gShellMapList. When reset all mappings,
1421 //all nodes in the gShellMapList will be free. Then gShellCurMapping will be a dangling pointer, So,
1422 //after created new mappings, we should reset the gShellCurMapping pointer back to node of current file system.
1423 //
1424 if (gShellCurMapping != NULL) {
1425 gShellCurMapping = NULL;
1426 CurDir = gEfiShellProtocol->GetEnv(L"cwd");
1427 if (CurDir != NULL) {
1428 MapName = AllocateCopyPool (StrSize(CurDir), CurDir);
1429 if (MapName == NULL) {
1430 return EFI_OUT_OF_RESOURCES;
1431 }
1432 SplitCurDir = StrStr (MapName, L":");
1433 if (SplitCurDir == NULL) {
1434 SHELL_FREE_NON_NULL (MapName);
1435 return EFI_UNSUPPORTED;
1436 }
1437 *(SplitCurDir + 1) = CHAR_NULL;
1438 MapListItem = ShellCommandFindMapItem (MapName);
1439 if (MapListItem != NULL) {
1440 gShellCurMapping = MapListItem;
1441 }
1442 SHELL_FREE_NON_NULL (MapName);
1443 }
1444 }
1445 } else {
1446 Count = (UINTN)-1;
1447 }
1448
1449 //
1450 // Find each handle with Block Io
1451 //
1452 HandleList = GetHandleListByProtocol(&gEfiBlockIoProtocolGuid);
1453 if (HandleList != NULL) {
1454 for (Count = 0 ; HandleList[Count] != NULL ; Count++);
1455
1456 //
1457 // Get all Device Paths
1458 //
1459 DevicePathList = AllocateZeroPool(sizeof(EFI_DEVICE_PATH_PROTOCOL*) * Count);
1460 if (DevicePathList == NULL) {
1461 SHELL_FREE_NON_NULL (HandleList);
1462 return EFI_OUT_OF_RESOURCES;
1463 }
1464
1465 for (Count = 0 ; HandleList[Count] != NULL ; Count++) {
1466 DevicePathList[Count] = DevicePathFromHandle(HandleList[Count]);
1467 }
1468
1469 //
1470 // Sort all DevicePaths
1471 //
1472 PerformQuickSort(DevicePathList, Count, sizeof(EFI_DEVICE_PATH_PROTOCOL*), DevicePathCompare);
1473
1474 //
1475 // Assign new Mappings to all...
1476 //
1477 for (Count = 0 ; HandleList[Count] != NULL ; Count++) {
1478 //
1479 // Get default name first
1480 //
1481 NewDefaultName = ShellCommandCreateNewMappingName(MappingTypeBlockIo);
1482 ASSERT(NewDefaultName != NULL);
1483 Status = ShellCommandAddMapItemAndUpdatePath(NewDefaultName, DevicePathList[Count], 0, FALSE);
1484 ASSERT_EFI_ERROR(Status);
1485 FreePool(NewDefaultName);
1486 }
1487
1488 SHELL_FREE_NON_NULL(HandleList);
1489 SHELL_FREE_NON_NULL(DevicePathList);
1490 } else if (Count == (UINTN)-1) {
1491 return (EFI_NOT_FOUND);
1492 }
1493
1494 return (EFI_SUCCESS);
1495 }
1496
1497 /**
1498 Add mappings for any devices without one. Do not change any existing maps.
1499
1500 @retval EFI_SUCCESS The operation was successful.
1501 **/
1502 EFI_STATUS
1503 EFIAPI
1504 ShellCommandUpdateMapping (
1505 VOID
1506 )
1507 {
1508 EFI_STATUS Status;
1509 EFI_HANDLE *HandleList;
1510 UINTN Count;
1511 EFI_DEVICE_PATH_PROTOCOL **DevicePathList;
1512 CHAR16 *NewDefaultName;
1513 CHAR16 *NewConsistName;
1514 EFI_DEVICE_PATH_PROTOCOL **ConsistMappingTable;
1515
1516 HandleList = NULL;
1517 Status = EFI_SUCCESS;
1518
1519 //
1520 // remove mappings that represent removed devices.
1521 //
1522
1523 //
1524 // Find each handle with Simple File System
1525 //
1526 HandleList = GetHandleListByProtocol(&gEfiSimpleFileSystemProtocolGuid);
1527 if (HandleList != NULL) {
1528 //
1529 // Do a count of the handles
1530 //
1531 for (Count = 0 ; HandleList[Count] != NULL ; Count++);
1532
1533 //
1534 // Get all Device Paths
1535 //
1536 DevicePathList = AllocateZeroPool(sizeof(EFI_DEVICE_PATH_PROTOCOL*) * Count);
1537 if (DevicePathList == NULL) {
1538 return (EFI_OUT_OF_RESOURCES);
1539 }
1540
1541 for (Count = 0 ; HandleList[Count] != NULL ; Count++) {
1542 DevicePathList[Count] = DevicePathFromHandle(HandleList[Count]);
1543 }
1544
1545 //
1546 // Sort all DevicePaths
1547 //
1548 PerformQuickSort(DevicePathList, Count, sizeof(EFI_DEVICE_PATH_PROTOCOL*), DevicePathCompare);
1549
1550 ShellCommandConsistMappingInitialize(&ConsistMappingTable);
1551
1552 //
1553 // Assign new Mappings to remainders
1554 //
1555 for (Count = 0 ; !EFI_ERROR(Status) && HandleList[Count] != NULL && !EFI_ERROR(Status); Count++) {
1556 //
1557 // Skip ones that already have
1558 //
1559 if (gEfiShellProtocol->GetMapFromDevicePath(&DevicePathList[Count]) != NULL) {
1560 continue;
1561 }
1562 //
1563 // Get default name
1564 //
1565 NewDefaultName = ShellCommandCreateNewMappingName(MappingTypeFileSystem);
1566 if (NewDefaultName == NULL) {
1567 Status = EFI_OUT_OF_RESOURCES;
1568 break;
1569 }
1570
1571 //
1572 // Call shell protocol SetMap function now...
1573 //
1574 Status = gEfiShellProtocol->SetMap(DevicePathList[Count], NewDefaultName);
1575
1576 if (!EFI_ERROR(Status)) {
1577 //
1578 // Now do consistent name
1579 //
1580 NewConsistName = ShellCommandConsistMappingGenMappingName(DevicePathList[Count], ConsistMappingTable);
1581 if (NewConsistName != NULL) {
1582 Status = gEfiShellProtocol->SetMap(DevicePathList[Count], NewConsistName);
1583 FreePool(NewConsistName);
1584 }
1585 }
1586
1587 FreePool(NewDefaultName);
1588 }
1589 ShellCommandConsistMappingUnInitialize(ConsistMappingTable);
1590 SHELL_FREE_NON_NULL(HandleList);
1591 SHELL_FREE_NON_NULL(DevicePathList);
1592
1593 HandleList = NULL;
1594 } else {
1595 Count = (UINTN)-1;
1596 }
1597 //
1598 // Do it all over again for gEfiBlockIoProtocolGuid
1599 //
1600
1601 return (Status);
1602 }
1603
1604 /**
1605 Converts a SHELL_FILE_HANDLE to an EFI_FILE_PROTOCOL*.
1606
1607 @param[in] Handle The SHELL_FILE_HANDLE to convert.
1608
1609 @return a EFI_FILE_PROTOCOL* representing the same file.
1610 **/
1611 EFI_FILE_PROTOCOL*
1612 EFIAPI
1613 ConvertShellHandleToEfiFileProtocol(
1614 IN CONST SHELL_FILE_HANDLE Handle
1615 )
1616 {
1617 return ((EFI_FILE_PROTOCOL*)(Handle));
1618 }
1619
1620 /**
1621 Converts a EFI_FILE_PROTOCOL* to an SHELL_FILE_HANDLE.
1622
1623 @param[in] Handle The pointer to EFI_FILE_PROTOCOL to convert.
1624 @param[in] Path The path to the file for verification.
1625
1626 @return A SHELL_FILE_HANDLE representing the same file.
1627 @retval NULL There was not enough memory.
1628 **/
1629 SHELL_FILE_HANDLE
1630 EFIAPI
1631 ConvertEfiFileProtocolToShellHandle(
1632 IN CONST EFI_FILE_PROTOCOL *Handle,
1633 IN CONST CHAR16 *Path
1634 )
1635 {
1636 SHELL_COMMAND_FILE_HANDLE *Buffer;
1637 BUFFER_LIST *NewNode;
1638
1639 if (Path != NULL) {
1640 Buffer = AllocateZeroPool(sizeof(SHELL_COMMAND_FILE_HANDLE));
1641 if (Buffer == NULL) {
1642 return (NULL);
1643 }
1644 NewNode = AllocateZeroPool(sizeof(BUFFER_LIST));
1645 if (NewNode == NULL) {
1646 SHELL_FREE_NON_NULL(Buffer);
1647 return (NULL);
1648 }
1649 Buffer->FileHandle = (EFI_FILE_PROTOCOL*)Handle;
1650 Buffer->Path = StrnCatGrow(&Buffer->Path, NULL, Path, 0);
1651 if (Buffer->Path == NULL) {
1652 SHELL_FREE_NON_NULL(NewNode);
1653 SHELL_FREE_NON_NULL(Buffer);
1654 return (NULL);
1655 }
1656 NewNode->Buffer = Buffer;
1657
1658 InsertHeadList(&mFileHandleList.Link, &NewNode->Link);
1659 }
1660 return ((SHELL_FILE_HANDLE)(Handle));
1661 }
1662
1663 /**
1664 Find the path that was logged with the specified SHELL_FILE_HANDLE.
1665
1666 @param[in] Handle The SHELL_FILE_HANDLE to query on.
1667
1668 @return A pointer to the path for the file.
1669 **/
1670 CONST CHAR16*
1671 EFIAPI
1672 ShellFileHandleGetPath(
1673 IN CONST SHELL_FILE_HANDLE Handle
1674 )
1675 {
1676 BUFFER_LIST *Node;
1677
1678 for (Node = (BUFFER_LIST*)GetFirstNode(&mFileHandleList.Link)
1679 ; !IsNull(&mFileHandleList.Link, &Node->Link)
1680 ; Node = (BUFFER_LIST*)GetNextNode(&mFileHandleList.Link, &Node->Link)
1681 ){
1682 if ((Node->Buffer) && (((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->FileHandle == Handle)){
1683 return (((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->Path);
1684 }
1685 }
1686 return (NULL);
1687 }
1688
1689 /**
1690 Remove a SHELL_FILE_HANDLE from the list of SHELL_FILE_HANDLES.
1691
1692 @param[in] Handle The SHELL_FILE_HANDLE to remove.
1693
1694 @retval TRUE The item was removed.
1695 @retval FALSE The item was not found.
1696 **/
1697 BOOLEAN
1698 EFIAPI
1699 ShellFileHandleRemove(
1700 IN CONST SHELL_FILE_HANDLE Handle
1701 )
1702 {
1703 BUFFER_LIST *Node;
1704
1705 for (Node = (BUFFER_LIST*)GetFirstNode(&mFileHandleList.Link)
1706 ; !IsNull(&mFileHandleList.Link, &Node->Link)
1707 ; Node = (BUFFER_LIST*)GetNextNode(&mFileHandleList.Link, &Node->Link)
1708 ){
1709 if ((Node->Buffer) && (((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->FileHandle == Handle)){
1710 RemoveEntryList(&Node->Link);
1711 SHELL_FREE_NON_NULL(((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->Path);
1712 SHELL_FREE_NON_NULL(Node->Buffer);
1713 SHELL_FREE_NON_NULL(Node);
1714 return (TRUE);
1715 }
1716 }
1717 return (FALSE);
1718 }
1719
1720 /**
1721 Function to determine if a SHELL_FILE_HANDLE is at the end of the file.
1722
1723 This will NOT work on directories.
1724
1725 If Handle is NULL, then ASSERT.
1726
1727 @param[in] Handle the file handle
1728
1729 @retval TRUE the position is at the end of the file
1730 @retval FALSE the position is not at the end of the file
1731 **/
1732 BOOLEAN
1733 EFIAPI
1734 ShellFileHandleEof(
1735 IN SHELL_FILE_HANDLE Handle
1736 )
1737 {
1738 EFI_FILE_INFO *Info;
1739 UINT64 Pos;
1740 BOOLEAN RetVal;
1741
1742 //
1743 // ASSERT if Handle is NULL
1744 //
1745 ASSERT(Handle != NULL);
1746
1747 gEfiShellProtocol->GetFilePosition(Handle, &Pos);
1748 Info = gEfiShellProtocol->GetFileInfo (Handle);
1749 gEfiShellProtocol->SetFilePosition(Handle, Pos);
1750
1751 if (Info == NULL) {
1752 return (FALSE);
1753 }
1754
1755 if (Pos == Info->FileSize) {
1756 RetVal = TRUE;
1757 } else {
1758 RetVal = FALSE;
1759 }
1760
1761 FreePool (Info);
1762
1763 return (RetVal);
1764 }
1765
1766 /**
1767 Frees any BUFFER_LIST defined type.
1768
1769 @param[in] List The BUFFER_LIST object to free.
1770 **/
1771 VOID
1772 EFIAPI
1773 FreeBufferList (
1774 IN BUFFER_LIST *List
1775 )
1776 {
1777 BUFFER_LIST *BufferListEntry;
1778
1779 if (List == NULL){
1780 return;
1781 }
1782 //
1783 // enumerate through the buffer list and free all memory
1784 //
1785 for ( BufferListEntry = ( BUFFER_LIST *)GetFirstNode(&List->Link)
1786 ; !IsListEmpty (&List->Link)
1787 ; BufferListEntry = (BUFFER_LIST *)GetFirstNode(&List->Link)
1788 ){
1789 RemoveEntryList(&BufferListEntry->Link);
1790 if (BufferListEntry->Buffer != NULL) {
1791 FreePool(BufferListEntry->Buffer);
1792 }
1793 FreePool(BufferListEntry);
1794 }
1795 }
1796
1797 /**
1798 Dump some hexadecimal data to the screen.
1799
1800 @param[in] Indent How many spaces to indent the output.
1801 @param[in] Offset The offset of the printing.
1802 @param[in] DataSize The size in bytes of UserData.
1803 @param[in] UserData The data to print out.
1804 **/
1805 VOID
1806 EFIAPI
1807 DumpHex (
1808 IN UINTN Indent,
1809 IN UINTN Offset,
1810 IN UINTN DataSize,
1811 IN VOID *UserData
1812 )
1813 {
1814 UINT8 *Data;
1815
1816 CHAR8 Val[50];
1817
1818 CHAR8 Str[20];
1819
1820 UINT8 TempByte;
1821 UINTN Size;
1822 UINTN Index;
1823
1824 Data = UserData;
1825 while (DataSize != 0) {
1826 Size = 16;
1827 if (Size > DataSize) {
1828 Size = DataSize;
1829 }
1830
1831 for (Index = 0; Index < Size; Index += 1) {
1832 TempByte = Data[Index];
1833 Val[Index * 3 + 0] = Hex[TempByte >> 4];
1834 Val[Index * 3 + 1] = Hex[TempByte & 0xF];
1835 Val[Index * 3 + 2] = (CHAR8) ((Index == 7) ? '-' : ' ');
1836 Str[Index] = (CHAR8) ((TempByte < ' ' || TempByte > '~') ? '.' : TempByte);
1837 }
1838
1839 Val[Index * 3] = 0;
1840 Str[Index] = 0;
1841 ShellPrintEx(-1, -1, L"%*a%08X: %-48a *%a*\r\n", Indent, "", Offset, Val, Str);
1842
1843 Data += Size;
1844 Offset += Size;
1845 DataSize -= Size;
1846 }
1847 }
1848
1849 /**
1850 Dump HEX data into buffer.
1851
1852 @param[in] Buffer HEX data to be dumped in Buffer.
1853 @param[in] Indent How many spaces to indent the output.
1854 @param[in] Offset The offset of the printing.
1855 @param[in] DataSize The size in bytes of UserData.
1856 @param[in] UserData The data to print out.
1857 **/
1858 CHAR16*
1859 EFIAPI
1860 CatSDumpHex (
1861 IN CHAR16 *Buffer,
1862 IN UINTN Indent,
1863 IN UINTN Offset,
1864 IN UINTN DataSize,
1865 IN VOID *UserData
1866 )
1867 {
1868 UINT8 *Data;
1869 UINT8 TempByte;
1870 UINTN Size;
1871 UINTN Index;
1872 CHAR8 Val[50];
1873 CHAR8 Str[20];
1874 CHAR16 *RetVal;
1875 CHAR16 *TempRetVal;
1876
1877 Data = UserData;
1878 RetVal = Buffer;
1879 while (DataSize != 0) {
1880 Size = 16;
1881 if (Size > DataSize) {
1882 Size = DataSize;
1883 }
1884
1885 for (Index = 0; Index < Size; Index += 1) {
1886 TempByte = Data[Index];
1887 Val[Index * 3 + 0] = Hex[TempByte >> 4];
1888 Val[Index * 3 + 1] = Hex[TempByte & 0xF];
1889 Val[Index * 3 + 2] = (CHAR8) ((Index == 7) ? '-' : ' ');
1890 Str[Index] = (CHAR8) ((TempByte < ' ' || TempByte > 'z') ? '.' : TempByte);
1891 }
1892
1893 Val[Index * 3] = 0;
1894 Str[Index] = 0;
1895 TempRetVal = CatSPrint (RetVal, L"%*a%08X: %-48a *%a*\r\n", Indent, "", Offset, Val, Str);
1896 SHELL_FREE_NON_NULL (RetVal);
1897 RetVal = TempRetVal;
1898
1899 Data += Size;
1900 Offset += Size;
1901 DataSize -= Size;
1902 }
1903
1904 return RetVal;
1905 }