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