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