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