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