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