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