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