• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2   Main file for Help shell level 3 function.
3 
4   Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved. <BR>
5   Copyright (c) 2014, ARM Limited. All rights reserved. <BR>
6   (C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>
7 
8   This program and the accompanying materials
9   are licensed and made available under the terms and conditions of the BSD License
10   which accompanies this distribution.  The full text of the license may be found at
11   http://opensource.org/licenses/bsd-license.php
12 
13   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
14   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15 
16 **/
17 
18 #include "UefiShellLevel3CommandsLib.h"
19 
20 #include <Library/ShellLib.h>
21 #include <Library/HandleParsingLib.h>
22 
23 #include <Protocol/ShellDynamicCommand.h>
24 
25 /**
26    function to insert string items into a list in the correct alphabetical place
27 
28    the resultant list is a double NULL terminated list of NULL terminated strings.
29 
30    upon successful return the memory must be caller freed (unless passed back in
31    via a loop where it will get reallocated).
32 
33    @param[in,out] DestList    double pointer to the list. may be NULL.
34    @param[in,out] DestSize    pointer to the size of list. may be 0, if DestList is NULL.
35    @param[in]     Item        the item to insert.
36 
37    @retval EFI_SUCCESS        the operation was successful.
38 **/
39 EFI_STATUS
LexicalInsertIntoList(IN OUT CHAR16 ** DestList,IN OUT UINTN * DestSize,IN CONST CHAR16 * Item)40 LexicalInsertIntoList(
41   IN OUT   CHAR16 **DestList,
42   IN OUT   UINTN  *DestSize,
43   IN CONST CHAR16 *Item
44   )
45 {
46   CHAR16                              *NewList;
47   INTN                                LexicalMatchValue;
48   CHAR16                              *LexicalSpot;
49   UINTN                               SizeOfAddedNameInBytes;
50 
51   //
52   // If there are none, then just return with success
53   //
54   if (Item == NULL || *Item == CHAR_NULL || StrLen(Item)==0) {
55     return (EFI_SUCCESS);
56   }
57 
58   NewList = *DestList;
59 
60   SizeOfAddedNameInBytes = StrSize(Item);
61   NewList = ReallocatePool(*DestSize, (*DestSize) + SizeOfAddedNameInBytes, NewList);
62   (*DestSize) = (*DestSize) + SizeOfAddedNameInBytes;
63 
64   //
65   // Find the correct spot in the list
66   //
67   for (LexicalSpot = NewList
68     ; LexicalSpot != NULL && LexicalSpot < NewList + (*DestSize)
69     ; LexicalSpot += StrLen(LexicalSpot) + 1
70     ) {
71     //
72     // Get Lexical Comparison Value between PrevCommand and Command list entry
73     //
74     LexicalMatchValue = gUnicodeCollation->StriColl (
75                                               gUnicodeCollation,
76                                               (CHAR16 *)LexicalSpot,
77                                               (CHAR16 *)Item
78                                               );
79     //
80     // The new item goes before this one.
81     //
82     if (LexicalMatchValue > 0 || StrLen(LexicalSpot) == 0) {
83       if (StrLen(LexicalSpot) != 0) {
84         //
85         // Move this and all other items out of the way
86         //
87         CopyMem(
88           LexicalSpot + (SizeOfAddedNameInBytes/sizeof(CHAR16)),
89           LexicalSpot,
90           (*DestSize) - SizeOfAddedNameInBytes - ((LexicalSpot - NewList) * sizeof(CHAR16))
91           );
92       }
93 
94       //
95       // Stick this one in place
96       //
97       StrCpyS(LexicalSpot, SizeOfAddedNameInBytes/sizeof(CHAR16), Item);
98       break;
99     }
100   }
101 
102   *DestList = NewList;
103   return (EFI_SUCCESS);
104 }
105 
106 /**
107    function to add each command name from the linked list to the string list.
108 
109    the resultant list is a double NULL terminated list of NULL terminated strings.
110 
111    @param[in,out] DestList    double pointer to the list. may be NULL.
112    @param[in,out] DestSize    pointer to the size of list. may be 0, if DestList is NULL.
113    @param[in]     SourceList  the double linked list of commands.
114 
115    @retval EFI_SUCCESS        the operation was successful.
116 **/
117 EFI_STATUS
CopyListOfCommandNames(IN OUT CHAR16 ** DestList,IN OUT UINTN * DestSize,IN CONST COMMAND_LIST * SourceList)118 CopyListOfCommandNames(
119   IN OUT   CHAR16       **DestList,
120   IN OUT   UINTN        *DestSize,
121   IN CONST COMMAND_LIST *SourceList
122   )
123 {
124   CONST COMMAND_LIST  *Node;
125 
126   for ( Node = (COMMAND_LIST*)GetFirstNode(&SourceList->Link)
127       ; SourceList != NULL && !IsListEmpty(&SourceList->Link) && !IsNull(&SourceList->Link, &Node->Link)
128       ; Node = (COMMAND_LIST*)GetNextNode(&SourceList->Link, &Node->Link)
129     ) {
130     LexicalInsertIntoList(DestList, DestSize, Node->CommandString);
131   }
132   return (EFI_SUCCESS);
133 }
134 
135 /**
136    function to add each dynamic command name to the string list.
137 
138    the resultant list is a double NULL terminated list of NULL terminated strings.
139 
140    @param[in,out] DestList    double pointer to the list. may be NULL.
141    @param[in,out] DestSize    pointer to the size of list. may be 0, if DestList is NULL.
142 
143    @retval EFI_SUCCESS        the operation was successful.
144    @return an error from HandleProtocol
145 **/
146 STATIC
147 EFI_STATUS
CopyListOfCommandNamesWithDynamic(IN OUT CHAR16 ** DestList,IN OUT UINTN * DestSize)148 CopyListOfCommandNamesWithDynamic(
149   IN OUT  CHAR16** DestList,
150   IN OUT  UINTN    *DestSize
151   )
152 {
153   EFI_HANDLE                          *CommandHandleList;
154   CONST EFI_HANDLE                    *NextCommand;
155   EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL  *DynamicCommand;
156   EFI_STATUS                          Status;
157 
158   CommandHandleList = GetHandleListByProtocol(&gEfiShellDynamicCommandProtocolGuid);
159 
160   //
161   // If there are none, then just return with success
162   //
163   if (CommandHandleList == NULL) {
164     return (EFI_SUCCESS);
165   }
166 
167   Status = EFI_SUCCESS;
168 
169   //
170   // Append those to the list.
171   //
172   for (NextCommand = CommandHandleList ; *NextCommand != NULL && !EFI_ERROR(Status) ; NextCommand++) {
173     Status = gBS->HandleProtocol(
174       *NextCommand,
175       &gEfiShellDynamicCommandProtocolGuid,
176       (VOID **)&DynamicCommand
177       );
178 
179     if (EFI_ERROR(Status)) {
180       continue;
181     }
182 
183     Status = LexicalInsertIntoList(DestList, DestSize, DynamicCommand->CommandName);
184   }
185 
186   SHELL_FREE_NON_NULL(CommandHandleList);
187   return (Status);
188 }
189 
190 
191 /**
192   Attempt to print help from a dynamically added command.
193 
194   @param[in]  CommandToGetHelpOn  The unicode name of the command that help is
195                                   requested on.
196   @param[in]  SectionToGetHelpOn  Pointer to the section specifier(s).
197   @param[in]  PrintCommandText    Print the command followed by the help content
198                                   or just help.
199 
200   @retval EFI_SUCCESS             The help was displayed
201   @retval EFI_NOT_FOUND           The command name could not be found
202   @retval EFI_DEVICE_ERROR        The help data format was incorrect.
203 **/
204 EFI_STATUS
PrintDynamicCommandHelp(IN CONST CHAR16 * CommandToGetHelpOn,IN CONST CHAR16 * SectionToGetHelpOn,IN BOOLEAN PrintCommandText)205 PrintDynamicCommandHelp(
206   IN CONST CHAR16  *CommandToGetHelpOn,
207   IN CONST CHAR16  *SectionToGetHelpOn,
208   IN BOOLEAN       PrintCommandText
209  )
210 {
211   EFI_STATUS                          Status;
212   BOOLEAN                             Found;
213   EFI_HANDLE                          *CommandHandleList;
214   EFI_HANDLE                          *NextCommand;
215   EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL  *DynamicCommand;
216 
217   Status = EFI_NOT_FOUND;
218   Found = FALSE;
219   CommandHandleList = NULL;
220 
221   CommandHandleList = GetHandleListByProtocol(&gEfiShellDynamicCommandProtocolGuid);
222 
223   if (CommandHandleList == NULL) {
224     //
225     // not found or out of resources
226     //
227     return Status;
228   }
229 
230   for (NextCommand = CommandHandleList; *NextCommand != NULL; NextCommand++) {
231     Status = gBS->HandleProtocol(
232       *NextCommand,
233       &gEfiShellDynamicCommandProtocolGuid,
234       (VOID **)&DynamicCommand
235       );
236 
237     if (EFI_ERROR(Status)) {
238       continue;
239     }
240 
241     //
242     // Check execution break flag when printing multiple command help information.
243     //
244     if (ShellGetExecutionBreakFlag ()) {
245       break;
246     }
247 
248     if ((gUnicodeCollation->MetaiMatch (gUnicodeCollation, (CHAR16 *)DynamicCommand->CommandName, (CHAR16*)CommandToGetHelpOn)) ||
249       (gEfiShellProtocol->GetAlias (CommandToGetHelpOn, NULL) != NULL && (gUnicodeCollation->MetaiMatch (gUnicodeCollation, (CHAR16 *)DynamicCommand->CommandName, (CHAR16*)(gEfiShellProtocol->GetAlias(CommandToGetHelpOn, NULL)))))) {
250       // Print as Shell Help if in ManPage format.
251       Status = ShellPrintHelp (DynamicCommand->CommandName, SectionToGetHelpOn,
252                               PrintCommandText);
253       if (Status == EFI_DEVICE_ERROR) {
254         ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_HELP_INV),
255                         gShellLevel3HiiHandle, DynamicCommand->CommandName);
256       } else if (EFI_ERROR(Status)) {
257         ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_HELP_NF),
258                         gShellLevel3HiiHandle, DynamicCommand->CommandName);
259       } else {
260         Found = TRUE;
261       }
262     }
263   }
264 
265   SHELL_FREE_NON_NULL(CommandHandleList);
266 
267   return (Found ? EFI_SUCCESS : Status);
268 
269 }
270 
271 STATIC CONST SHELL_PARAM_ITEM ParamList[] = {
272   {L"-usage", TypeFlag},
273   {L"-section", TypeMaxValue},
274   {L"-verbose", TypeFlag},
275   {L"-v", TypeFlag},
276   {NULL, TypeMax}
277   };
278 
279 /**
280   Function for 'help' command.
281 
282   @param[in] ImageHandle  Handle to the Image (NULL if Internal).
283   @param[in] SystemTable  Pointer to the System Table (NULL if Internal).
284 **/
285 SHELL_STATUS
286 EFIAPI
ShellCommandRunHelp(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)287 ShellCommandRunHelp (
288   IN EFI_HANDLE        ImageHandle,
289   IN EFI_SYSTEM_TABLE  *SystemTable
290   )
291 {
292   EFI_STATUS          Status;
293   LIST_ENTRY          *Package;
294   CHAR16              *ProblemParam;
295   SHELL_STATUS        ShellStatus;
296   CHAR16              *SortedCommandList;
297   CONST CHAR16        *CurrentCommand;
298   CHAR16              *CommandToGetHelpOn;
299   CHAR16              *SectionToGetHelpOn;
300   CHAR16              *HiiString;
301   BOOLEAN             Found;
302   BOOLEAN             PrintCommandText;
303   UINTN               SortedCommandListSize;
304 
305   PrintCommandText    = TRUE;
306   ProblemParam        = NULL;
307   ShellStatus         = SHELL_SUCCESS;
308   CommandToGetHelpOn  = NULL;
309   SectionToGetHelpOn  = NULL;
310   SortedCommandList   = NULL;
311   Found               = FALSE;
312 
313   //
314   // initialize the shell lib (we must be in non-auto-init...)
315   //
316   Status = ShellInitialize();
317   ASSERT_EFI_ERROR(Status);
318 
319   Status = CommandInit();
320   ASSERT_EFI_ERROR(Status);
321 
322   //
323   // parse the command line
324   //
325   Status = ShellCommandLineParse (ParamList, &Package, &ProblemParam, TRUE);
326   if (EFI_ERROR(Status)) {
327     if (Status == EFI_VOLUME_CORRUPTED && ProblemParam != NULL) {
328       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), gShellLevel3HiiHandle, L"help", ProblemParam);
329       FreePool(ProblemParam);
330       ShellStatus = SHELL_INVALID_PARAMETER;
331     } else {
332       ASSERT(FALSE);
333     }
334   } else {
335     //
336     // Check for conflicting parameters.
337     //
338     if (ShellCommandLineGetFlag(Package, L"-usage")
339       &&ShellCommandLineGetFlag(Package, L"-section")
340       &&(ShellCommandLineGetFlag(Package, L"-verbose") || ShellCommandLineGetFlag(Package, L"-v"))
341      ){
342       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_CON), gShellLevel3HiiHandle, L"help");
343       ShellStatus = SHELL_INVALID_PARAMETER;
344     } else if (ShellCommandLineGetRawValue(Package, 2) != NULL) {
345       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_MANY), gShellLevel3HiiHandle, L"help");
346       ShellStatus = SHELL_INVALID_PARAMETER;
347     } else {
348       //
349       // Get the command name we are getting help on
350       //
351       ASSERT(CommandToGetHelpOn == NULL);
352       StrnCatGrow(&CommandToGetHelpOn, NULL, ShellCommandLineGetRawValue(Package, 1), 0);
353       if (CommandToGetHelpOn == NULL && ShellCommandLineGetFlag(Package, L"-?")) {
354         //
355         // If we dont have a command and we got a simple -?
356         // we are looking for help on help command.
357         //
358         StrnCatGrow(&CommandToGetHelpOn, NULL, L"help", 0);
359       }
360 
361       if (CommandToGetHelpOn == NULL) {
362         StrnCatGrow(&CommandToGetHelpOn, NULL, L"*", 0);
363         ASSERT(SectionToGetHelpOn == NULL);
364         StrnCatGrow(&SectionToGetHelpOn, NULL, L"NAME", 0);
365       } else {
366         PrintCommandText = FALSE;
367         ASSERT(SectionToGetHelpOn == NULL);
368         //
369         // Get the section name for the given command name
370         //
371         if (ShellCommandLineGetFlag(Package, L"-section")) {
372           StrnCatGrow(&SectionToGetHelpOn, NULL, ShellCommandLineGetValue(Package, L"-section"), 0);
373         } else if (ShellCommandLineGetFlag(Package, L"-usage")) {
374           StrnCatGrow(&SectionToGetHelpOn, NULL, L"NAME,SYNOPSIS", 0);
375         } else if (ShellCommandLineGetFlag(Package, L"-verbose") || ShellCommandLineGetFlag(Package, L"-v")) {
376         } else {
377           //
378           // The output of help <command> will display NAME, SYNOPSIS, OPTIONS, DESCRIPTION, and EXAMPLES sections.
379           //
380           StrnCatGrow (&SectionToGetHelpOn, NULL, L"NAME,SYNOPSIS,OPTIONS,DESCRIPTION,EXAMPLES", 0);
381         }
382       }
383 
384       if (gUnicodeCollation->StriColl(gUnicodeCollation, CommandToGetHelpOn, L"special") == 0) {
385         //
386         // we need info on the special characters
387         //
388         ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_HELP_SC_HEADER), gShellLevel3HiiHandle);
389         HiiString = HiiGetString(gShellLevel3HiiHandle, STRING_TOKEN(STR_HELP_SC_DATA), NULL);
390         ShellPrintEx(-1, -1, L"%s", HiiString);
391         FreePool(HiiString);
392         Found = TRUE;
393       } else {
394         SortedCommandList = NULL;
395         SortedCommandListSize = 0;
396         CopyListOfCommandNames(&SortedCommandList, &SortedCommandListSize, ShellCommandGetCommandList(TRUE));
397         CopyListOfCommandNamesWithDynamic(&SortedCommandList, &SortedCommandListSize);
398 
399         for (CurrentCommand = SortedCommandList
400           ; CurrentCommand != NULL && *CurrentCommand != CHAR_NULL && CurrentCommand < SortedCommandList + SortedCommandListSize/sizeof(CHAR16)
401           ; CurrentCommand += StrLen(CurrentCommand) + 1
402           ) {
403           //
404           // Checking execution break flag when print multiple command help information.
405           //
406           if (ShellGetExecutionBreakFlag ()) {
407             break;
408           }
409 
410           if ((gUnicodeCollation->MetaiMatch(gUnicodeCollation, (CHAR16*)CurrentCommand, CommandToGetHelpOn)) ||
411              (gEfiShellProtocol->GetAlias(CommandToGetHelpOn, NULL) != NULL && (gUnicodeCollation->MetaiMatch(gUnicodeCollation, (CHAR16*)CurrentCommand, (CHAR16*)(gEfiShellProtocol->GetAlias(CommandToGetHelpOn, NULL)))))) {
412             //
413             // We have a command to look for help on.
414             //
415             Status = ShellPrintHelp(CurrentCommand, SectionToGetHelpOn, PrintCommandText);
416             if (EFI_ERROR(Status)) {
417               //
418               // now try to match against the dynamic command list and print help
419               //
420               Status = PrintDynamicCommandHelp (CurrentCommand, SectionToGetHelpOn, PrintCommandText);
421             }
422             if (Status == EFI_DEVICE_ERROR) {
423                 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_HELP_INV), gShellLevel3HiiHandle, CurrentCommand);
424             } else if (EFI_ERROR(Status)) {
425                 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_HELP_NF), gShellLevel3HiiHandle, CurrentCommand);
426             } else {
427                 Found = TRUE;
428             }
429           }
430         }
431 
432         //
433         // Search the .man file for Shell applications (Shell external commands).
434         //
435         if (!Found) {
436           Status = ShellPrintHelp(CommandToGetHelpOn, SectionToGetHelpOn, FALSE);
437           if (Status == EFI_DEVICE_ERROR) {
438               ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_HELP_INV), gShellLevel3HiiHandle, CommandToGetHelpOn);
439           } else if (EFI_ERROR(Status)) {
440               ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_HELP_NF), gShellLevel3HiiHandle, CommandToGetHelpOn);
441           } else {
442             Found = TRUE;
443           }
444         }
445       }
446 
447       if (!Found) {
448         ShellStatus = SHELL_NOT_FOUND;
449       }
450 
451       //
452       // free the command line package
453       //
454       ShellCommandLineFreeVarList (Package);
455     }
456   }
457 
458   if (CommandToGetHelpOn != NULL && StrCmp(CommandToGetHelpOn, L"*") == 0){
459     //
460     // If '*' then the command entered was 'Help' without qualifiers, This footer
461     // provides additional info on help switches
462     //
463     ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_HELP_FOOTER), gShellLevel3HiiHandle);
464   }
465   if (CommandToGetHelpOn != NULL) {
466     FreePool(CommandToGetHelpOn);
467   }
468   if (SectionToGetHelpOn != NULL) {
469     FreePool(SectionToGetHelpOn);
470   }
471   SHELL_FREE_NON_NULL(SortedCommandList);
472 
473   return (ShellStatus);
474 }
475