ShellManParser.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784
  1. /** @file
  2. Provides interface to shell MAN file parser.
  3. Copyright (c) 2009 - 2019, Intel Corporation. All rights reserved.<BR>
  4. Copyright 2015 Dell Inc.
  5. SPDX-License-Identifier: BSD-2-Clause-Patent
  6. **/
  7. #include "Shell.h"
  8. #define SHELL_MAN_HII_GUID \
  9. { \
  10. 0xf62ccd0c, 0x2449, 0x453c, { 0x8a, 0xcb, 0x8c, 0xc5, 0x7c, 0xf0, 0x2a, 0x97 } \
  11. }
  12. EFI_HII_HANDLE mShellManHiiHandle = NULL;
  13. EFI_HANDLE mShellManDriverHandle = NULL;
  14. SHELL_MAN_HII_VENDOR_DEVICE_PATH mShellManHiiDevicePath = {
  15. {
  16. {
  17. HARDWARE_DEVICE_PATH,
  18. HW_VENDOR_DP,
  19. {
  20. (UINT8)(sizeof (VENDOR_DEVICE_PATH)),
  21. (UINT8)((sizeof (VENDOR_DEVICE_PATH)) >> 8)
  22. }
  23. },
  24. SHELL_MAN_HII_GUID
  25. },
  26. {
  27. END_DEVICE_PATH_TYPE,
  28. END_ENTIRE_DEVICE_PATH_SUBTYPE,
  29. {
  30. (UINT8)(END_DEVICE_PATH_LENGTH),
  31. (UINT8)((END_DEVICE_PATH_LENGTH) >> 8)
  32. }
  33. }
  34. };
  35. /**
  36. Verifies that the filename has .EFI on the end.
  37. allocates a new buffer and copies the name (appending .EFI if necessary).
  38. Caller to free the buffer.
  39. @param[in] NameString original name string
  40. @return the new filename with .efi as the extension.
  41. **/
  42. CHAR16 *
  43. GetExecuatableFileName (
  44. IN CONST CHAR16 *NameString
  45. )
  46. {
  47. CHAR16 *Buffer;
  48. CHAR16 *SuffixStr;
  49. if (NameString == NULL) {
  50. return (NULL);
  51. }
  52. //
  53. // Fix the file name
  54. //
  55. if (StrnCmp (NameString+StrLen (NameString)-StrLen (L".efi"), L".efi", StrLen (L".efi")) == 0) {
  56. Buffer = AllocateCopyPool (StrSize (NameString), NameString);
  57. } else if (StrnCmp (NameString+StrLen (NameString)-StrLen (L".man"), L".man", StrLen (L".man")) == 0) {
  58. Buffer = AllocateCopyPool (StrSize (NameString), NameString);
  59. if (Buffer != NULL) {
  60. SuffixStr = Buffer+StrLen (Buffer)-StrLen (L".man");
  61. StrnCpyS (SuffixStr, StrSize (L".man")/sizeof (CHAR16), L".efi", StrLen (L".efi"));
  62. }
  63. } else {
  64. Buffer = AllocateZeroPool (StrSize (NameString) + StrLen (L".efi")*sizeof (CHAR16));
  65. if (Buffer != NULL) {
  66. StrnCpyS (
  67. Buffer,
  68. (StrSize (NameString) + StrLen (L".efi")*sizeof (CHAR16))/sizeof (CHAR16),
  69. NameString,
  70. StrLen (NameString)
  71. );
  72. StrnCatS (
  73. Buffer,
  74. (StrSize (NameString) + StrLen (L".efi")*sizeof (CHAR16))/sizeof (CHAR16),
  75. L".efi",
  76. StrLen (L".efi")
  77. );
  78. }
  79. }
  80. return (Buffer);
  81. }
  82. /**
  83. Verifies that the filename has .MAN on the end.
  84. allocates a new buffer and copies the name (appending .MAN if necessary)
  85. ASSERT if ManFileName is NULL
  86. @param[in] ManFileName original filename
  87. @return the new filename with .man as the extension.
  88. **/
  89. CHAR16 *
  90. GetManFileName (
  91. IN CONST CHAR16 *ManFileName
  92. )
  93. {
  94. CHAR16 *Buffer;
  95. if (ManFileName == NULL) {
  96. return (NULL);
  97. }
  98. //
  99. // Fix the file name
  100. //
  101. if (StrnCmp (ManFileName+StrLen (ManFileName)-4, L".man", 4) == 0) {
  102. Buffer = AllocateCopyPool (StrSize (ManFileName), ManFileName);
  103. } else {
  104. Buffer = AllocateZeroPool (StrSize (ManFileName) + 4*sizeof (CHAR16));
  105. if (Buffer != NULL) {
  106. StrnCpyS (
  107. Buffer,
  108. (StrSize (ManFileName) + 4*sizeof (CHAR16))/sizeof (CHAR16),
  109. ManFileName,
  110. StrLen (ManFileName)
  111. );
  112. StrnCatS (
  113. Buffer,
  114. (StrSize (ManFileName) + 4*sizeof (CHAR16))/sizeof (CHAR16),
  115. L".man",
  116. 4
  117. );
  118. }
  119. }
  120. return (Buffer);
  121. }
  122. /**
  123. Search the path environment variable for possible locations and test for
  124. which one contains a man file with the name specified. If a valid file is found
  125. stop searching and return the (opened) SHELL_FILE_HANDLE for that file.
  126. @param[in] FileName Name of the file to find and open.
  127. @param[out] Handle Pointer to the handle of the found file. The
  128. value of this is undefined for return values
  129. except EFI_SUCCESS.
  130. @retval EFI_SUCCESS The file was found. Handle is a valid SHELL_FILE_HANDLE
  131. @retval EFI_INVALID_PARAMETER A parameter had an invalid value.
  132. @retval EFI_NOT_FOUND The file was not found.
  133. **/
  134. EFI_STATUS
  135. SearchPathForFile (
  136. IN CONST CHAR16 *FileName,
  137. OUT SHELL_FILE_HANDLE *Handle
  138. )
  139. {
  140. CHAR16 *FullFileName;
  141. EFI_STATUS Status;
  142. if ( (FileName == NULL)
  143. || (Handle == NULL)
  144. || (StrLen (FileName) == 0)
  145. )
  146. {
  147. return (EFI_INVALID_PARAMETER);
  148. }
  149. FullFileName = ShellFindFilePath (FileName);
  150. if (FullFileName == NULL) {
  151. return (EFI_NOT_FOUND);
  152. }
  153. //
  154. // now open that file
  155. //
  156. Status = EfiShellOpenFileByName (FullFileName, Handle, EFI_FILE_MODE_READ);
  157. FreePool (FullFileName);
  158. return (Status);
  159. }
  160. /**
  161. Parses through the MAN file specified by SHELL_FILE_HANDLE and returns the
  162. detailed help for any sub section specified in the comma separated list of
  163. sections provided. If the end of the file or a .TH section is found then
  164. return.
  165. Upon a successful return the caller is responsible to free the memory in *HelpText
  166. @param[in] Handle FileHandle to read from
  167. @param[in] Sections name of command's sub sections to find
  168. @param[out] HelpText pointer to pointer to string where text goes.
  169. @param[out] HelpSize pointer to size of allocated HelpText (may be updated)
  170. @param[in] Ascii TRUE if the file is ASCII, FALSE otherwise.
  171. @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
  172. @retval EFI_SUCCESS the section was found and its description stored in
  173. an allocated buffer.
  174. **/
  175. EFI_STATUS
  176. ManFileFindSections (
  177. IN SHELL_FILE_HANDLE Handle,
  178. IN CONST CHAR16 *Sections,
  179. OUT CHAR16 **HelpText,
  180. OUT UINTN *HelpSize,
  181. IN BOOLEAN Ascii
  182. )
  183. {
  184. EFI_STATUS Status;
  185. CHAR16 *ReadLine;
  186. UINTN Size;
  187. BOOLEAN CurrentlyReading;
  188. CHAR16 *SectionName;
  189. UINTN SectionLen;
  190. BOOLEAN Found;
  191. if ( (Handle == NULL)
  192. || (HelpText == NULL)
  193. || (HelpSize == NULL)
  194. )
  195. {
  196. return (EFI_INVALID_PARAMETER);
  197. }
  198. Status = EFI_SUCCESS;
  199. CurrentlyReading = FALSE;
  200. Size = 1024;
  201. Found = FALSE;
  202. ReadLine = AllocateZeroPool (Size);
  203. if (ReadLine == NULL) {
  204. return (EFI_OUT_OF_RESOURCES);
  205. }
  206. for ( ; !ShellFileHandleEof (Handle); Size = 1024) {
  207. Status = ShellFileHandleReadLine (Handle, ReadLine, &Size, TRUE, &Ascii);
  208. if (ReadLine[0] == L'#') {
  209. //
  210. // Skip comment lines
  211. //
  212. continue;
  213. }
  214. //
  215. // ignore too small of buffer...
  216. //
  217. if (Status == EFI_BUFFER_TOO_SMALL) {
  218. Status = EFI_SUCCESS;
  219. }
  220. if (EFI_ERROR (Status)) {
  221. break;
  222. } else if (StrnCmp (ReadLine, L".TH", 3) == 0) {
  223. //
  224. // we hit the end of this commands section so stop.
  225. //
  226. break;
  227. } else if (StrnCmp (ReadLine, L".SH", 3) == 0) {
  228. if (Sections == NULL) {
  229. CurrentlyReading = TRUE;
  230. continue;
  231. }
  232. //
  233. // we found a section
  234. //
  235. if (CurrentlyReading) {
  236. CurrentlyReading = FALSE;
  237. }
  238. //
  239. // is this a section we want to read in?
  240. //
  241. for ( SectionName = ReadLine + 3
  242. ; *SectionName == L' '
  243. ; SectionName++)
  244. {
  245. }
  246. SectionLen = StrLen (SectionName);
  247. SectionName = StrStr (Sections, SectionName);
  248. if (SectionName == NULL) {
  249. continue;
  250. }
  251. if ((*(SectionName + SectionLen) == CHAR_NULL) || (*(SectionName + SectionLen) == L',')) {
  252. CurrentlyReading = TRUE;
  253. }
  254. } else if (CurrentlyReading) {
  255. Found = TRUE;
  256. //
  257. // copy and save the current line.
  258. //
  259. ASSERT ((*HelpText == NULL && *HelpSize == 0) || (*HelpText != NULL));
  260. StrnCatGrow (HelpText, HelpSize, ReadLine, 0);
  261. StrnCatGrow (HelpText, HelpSize, L"\r\n", 0);
  262. }
  263. }
  264. FreePool (ReadLine);
  265. if (!Found && !EFI_ERROR (Status)) {
  266. return (EFI_NOT_FOUND);
  267. }
  268. return (Status);
  269. }
  270. /**
  271. Parses a line from a MAN file to see if it is the Title Header. If it is, then
  272. if the "Brief Description" is desired, allocate a buffer for it and return a
  273. copy. Upon a successful return the caller is responsible to free the memory in
  274. *BriefDesc
  275. Uses a simple state machine that allows "unlimited" whitespace before and after the
  276. ".TH", compares Command and the MAN file command name without respect to case, and
  277. allows "unlimited" whitespace and '0' and '1' characters before the Short Description.
  278. The PCRE regex describing this functionality is: ^\s*\.TH\s+(\S)\s[\s01]*(.*)$
  279. where group 1 is the Command Name and group 2 is the Short Description.
  280. @param[in] Command name of command whose MAN file we think Line came from
  281. @param[in] Line Pointer to a line from the MAN file
  282. @param[out] BriefDesc pointer to pointer to string where description goes.
  283. @param[out] BriefSize pointer to size of allocated BriefDesc
  284. @param[out] Found TRUE if the Title Header was found and it belongs to Command
  285. @retval TRUE Line contained the Title Header
  286. @retval FALSE Line did not contain the Title Header
  287. **/
  288. BOOLEAN
  289. IsTitleHeader (
  290. IN CONST CHAR16 *Command,
  291. IN CHAR16 *Line,
  292. OUT CHAR16 **BriefDesc OPTIONAL,
  293. OUT UINTN *BriefSize OPTIONAL,
  294. OUT BOOLEAN *Found
  295. )
  296. {
  297. // The states of a simple state machine used to recognize a title header line
  298. // and to extract the Short Description, if desired.
  299. typedef enum {
  300. LookForThMacro, LookForCommandName, CompareCommands, GetBriefDescription, Final
  301. } STATEVALUES;
  302. STATEVALUES State;
  303. UINTN CommandIndex; // Indexes Command as we compare its chars to the MAN file.
  304. BOOLEAN ReturnValue; // TRUE if this the Title Header line of *some* MAN file.
  305. BOOLEAN ReturnFound; // TRUE if this the Title Header line of *the desired* MAN file.
  306. ReturnValue = FALSE;
  307. ReturnFound = FALSE;
  308. CommandIndex = 0;
  309. State = LookForThMacro;
  310. do {
  311. if (*Line == L'\0') {
  312. break;
  313. }
  314. switch (State) {
  315. // Handle "^\s*.TH\s"
  316. // Go to state LookForCommandName if the title header macro is present; otherwise,
  317. // eat white space. If we see something other than white space, this is not a
  318. // title header line.
  319. case LookForThMacro:
  320. if ((StrnCmp (L".TH ", Line, 4) == 0) || (StrnCmp (L".TH\t", Line, 4) == 0)) {
  321. Line += 4;
  322. State = LookForCommandName;
  323. } else if ((*Line == L' ') || (*Line == L'\t')) {
  324. Line++;
  325. } else {
  326. State = Final;
  327. }
  328. break;
  329. // Handle "\s*"
  330. // Eat any "extra" whitespace after the title header macro (we have already seen
  331. // at least one white space character). Go to state CompareCommands when a
  332. // non-white space is seen.
  333. case LookForCommandName:
  334. if ((*Line == L' ') || (*Line == L'\t')) {
  335. Line++;
  336. } else {
  337. ReturnValue = TRUE; // This is *some* command's title header line.
  338. State = CompareCommands;
  339. // Do not increment Line; it points to the first character of the command
  340. // name on the title header line.
  341. }
  342. break;
  343. // Handle "(\S)\s"
  344. // Compare Command to the title header command name, ignoring case. When we
  345. // reach the end of the command (i.e. we see white space), the next state
  346. // depends on whether the caller wants a copy of the Brief Description.
  347. case CompareCommands:
  348. if ((*Line == L' ') || (*Line == L'\t')) {
  349. ReturnFound = TRUE; // This is the desired command's title header line.
  350. State = (BriefDesc == NULL) ? Final : GetBriefDescription;
  351. } else if (CharToUpper (*Line) != CharToUpper (*(Command + CommandIndex++))) {
  352. State = Final;
  353. }
  354. Line++;
  355. break;
  356. // Handle "[\s01]*(.*)$"
  357. // Skip whitespace, '0', and '1' characters, if any, prior to the brief description.
  358. // Return the description to the caller.
  359. case GetBriefDescription:
  360. if ((*Line != L' ') && (*Line != L'\t') && (*Line != L'0') && (*Line != L'1')) {
  361. *BriefSize = StrSize (Line);
  362. *BriefDesc = AllocateZeroPool (*BriefSize);
  363. if (*BriefDesc != NULL) {
  364. StrCpyS (*BriefDesc, (*BriefSize)/sizeof (CHAR16), Line);
  365. }
  366. State = Final;
  367. }
  368. Line++;
  369. break;
  370. default:
  371. break;
  372. }
  373. } while (State < Final);
  374. *Found = ReturnFound;
  375. return ReturnValue;
  376. }
  377. /**
  378. Parses through the MAN file specified by SHELL_FILE_HANDLE and returns the
  379. "Brief Description" for the .TH section as specified by Command. If the
  380. command section is not found return EFI_NOT_FOUND.
  381. Upon a successful return the caller is responsible to free the memory in *BriefDesc
  382. @param[in] Handle FileHandle to read from
  383. @param[in] Command name of command's section to find as entered on the
  384. command line (may be a relative or absolute path or
  385. be in any case: upper, lower, or mixed in numerous ways!).
  386. @param[out] BriefDesc pointer to pointer to string where description goes.
  387. @param[out] BriefSize pointer to size of allocated BriefDesc
  388. @param[in, out] Ascii TRUE if the file is ASCII, FALSE otherwise, will be
  389. set if the file handle is at the 0 position.
  390. @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
  391. @retval EFI_SUCCESS the section was found and its description stored in
  392. an allocated buffer if requested.
  393. **/
  394. EFI_STATUS
  395. ManFileFindTitleSection (
  396. IN SHELL_FILE_HANDLE Handle,
  397. IN CONST CHAR16 *Command,
  398. OUT CHAR16 **BriefDesc OPTIONAL,
  399. OUT UINTN *BriefSize OPTIONAL,
  400. IN OUT BOOLEAN *Ascii
  401. )
  402. {
  403. EFI_STATUS Status;
  404. CHAR16 *ReadLine;
  405. UINTN Size;
  406. BOOLEAN Found;
  407. UINTN Start;
  408. if ( (Handle == NULL)
  409. || (Command == NULL)
  410. || ((BriefDesc != NULL) && (BriefSize == NULL))
  411. )
  412. {
  413. return (EFI_INVALID_PARAMETER);
  414. }
  415. Status = EFI_SUCCESS;
  416. Size = 1024;
  417. Found = FALSE;
  418. ReadLine = AllocateZeroPool (Size);
  419. if (ReadLine == NULL) {
  420. return (EFI_OUT_OF_RESOURCES);
  421. }
  422. //
  423. // Do not pass any leading path information that may be present to IsTitleHeader().
  424. //
  425. Start = StrLen (Command);
  426. while ( (Start != 0)
  427. && (*(Command + Start - 1) != L'\\')
  428. && (*(Command + Start - 1) != L'/')
  429. && (*(Command + Start - 1) != L':'))
  430. {
  431. --Start;
  432. }
  433. for ( ; !ShellFileHandleEof (Handle); Size = 1024) {
  434. Status = ShellFileHandleReadLine (Handle, ReadLine, &Size, TRUE, Ascii);
  435. //
  436. // ignore too small of buffer...
  437. //
  438. if (EFI_ERROR (Status) && (Status != EFI_BUFFER_TOO_SMALL)) {
  439. break;
  440. }
  441. Status = EFI_NOT_FOUND;
  442. if (IsTitleHeader (Command+Start, ReadLine, BriefDesc, BriefSize, &Found)) {
  443. Status = Found ? EFI_SUCCESS : EFI_NOT_FOUND;
  444. break;
  445. }
  446. }
  447. FreePool (ReadLine);
  448. return (Status);
  449. }
  450. /**
  451. This function returns the help information for the specified command. The help text
  452. will be parsed from a UEFI Shell manual page. (see UEFI Shell 2.0 Appendix B)
  453. If Sections is specified, then each section name listed will be compared in a casesensitive
  454. manner, to the section names described in Appendix B. If the section exists,
  455. it will be appended to the returned help text. If the section does not exist, no
  456. information will be returned. If Sections is NULL, then all help text information
  457. available will be returned.
  458. if BriefDesc is NULL, then the breif description will not be saved separately,
  459. but placed first in the main HelpText.
  460. @param[in] ManFileName Points to the NULL-terminated UEFI Shell MAN file name.
  461. @param[in] Command Points to the NULL-terminated UEFI Shell command name.
  462. @param[in] Sections Points to the NULL-terminated comma-delimited
  463. section names to return. If NULL, then all
  464. sections will be returned.
  465. @param[out] BriefDesc On return, points to a callee-allocated buffer
  466. containing brief description text.
  467. @param[out] HelpText On return, points to a callee-allocated buffer
  468. containing all specified help text.
  469. @retval EFI_SUCCESS The help text was returned.
  470. @retval EFI_OUT_OF_RESOURCES The necessary buffer could not be allocated to hold the
  471. returned help text.
  472. @retval EFI_INVALID_PARAMETER HelpText is NULL.
  473. @retval EFI_INVALID_PARAMETER ManFileName is invalid.
  474. @retval EFI_NOT_FOUND There is no help text available for Command.
  475. **/
  476. EFI_STATUS
  477. ProcessManFile (
  478. IN CONST CHAR16 *ManFileName,
  479. IN CONST CHAR16 *Command,
  480. IN CONST CHAR16 *Sections OPTIONAL,
  481. OUT CHAR16 **BriefDesc OPTIONAL,
  482. OUT CHAR16 **HelpText
  483. )
  484. {
  485. CHAR16 *TempString;
  486. SHELL_FILE_HANDLE FileHandle;
  487. EFI_HANDLE CmdFileImgHandle;
  488. EFI_STATUS Status;
  489. UINTN HelpSize;
  490. UINTN BriefSize;
  491. UINTN StringIdWalker;
  492. BOOLEAN Ascii;
  493. CHAR16 *CmdFileName;
  494. CHAR16 *CmdFilePathName;
  495. EFI_DEVICE_PATH_PROTOCOL *FileDevPath;
  496. EFI_DEVICE_PATH_PROTOCOL *DevPath;
  497. EFI_HII_PACKAGE_LIST_HEADER *PackageListHeader;
  498. if ( (ManFileName == NULL)
  499. || (Command == NULL)
  500. || (HelpText == NULL)
  501. )
  502. {
  503. return (EFI_INVALID_PARAMETER);
  504. }
  505. HelpSize = 0;
  506. BriefSize = 0;
  507. StringIdWalker = 0;
  508. TempString = NULL;
  509. Ascii = FALSE;
  510. CmdFileName = NULL;
  511. CmdFilePathName = NULL;
  512. CmdFileImgHandle = NULL;
  513. PackageListHeader = NULL;
  514. FileDevPath = NULL;
  515. DevPath = NULL;
  516. //
  517. // See if it's in HII first
  518. //
  519. TempString = ShellCommandGetCommandHelp (Command);
  520. if (TempString != NULL) {
  521. FileHandle = ConvertEfiFileProtocolToShellHandle (CreateFileInterfaceMem (TRUE), NULL);
  522. HelpSize = StrLen (TempString) * sizeof (CHAR16);
  523. ShellWriteFile (FileHandle, &HelpSize, TempString);
  524. ShellSetFilePosition (FileHandle, 0);
  525. HelpSize = 0;
  526. BriefSize = 0;
  527. Status = ManFileFindTitleSection (FileHandle, Command, BriefDesc, &BriefSize, &Ascii);
  528. if (!EFI_ERROR (Status) && (HelpText != NULL)) {
  529. Status = ManFileFindSections (FileHandle, Sections, HelpText, &HelpSize, Ascii);
  530. }
  531. ShellCloseFile (&FileHandle);
  532. } else {
  533. //
  534. // If the image is a external app, check .MAN file first.
  535. //
  536. FileHandle = NULL;
  537. TempString = GetManFileName (ManFileName);
  538. if (TempString == NULL) {
  539. return (EFI_INVALID_PARAMETER);
  540. }
  541. Status = SearchPathForFile (TempString, &FileHandle);
  542. if (EFI_ERROR (Status)) {
  543. FileDevPath = FileDevicePath (NULL, TempString);
  544. DevPath = AppendDevicePath (ShellInfoObject.ImageDevPath, FileDevPath);
  545. Status = InternalOpenFileDevicePath (DevPath, &FileHandle, EFI_FILE_MODE_READ, 0);
  546. SHELL_FREE_NON_NULL (FileDevPath);
  547. SHELL_FREE_NON_NULL (DevPath);
  548. }
  549. if (!EFI_ERROR (Status)) {
  550. HelpSize = 0;
  551. BriefSize = 0;
  552. Status = ManFileFindTitleSection (FileHandle, Command, BriefDesc, &BriefSize, &Ascii);
  553. if (!EFI_ERROR (Status) && (HelpText != NULL)) {
  554. Status = ManFileFindSections (FileHandle, Sections, HelpText, &HelpSize, Ascii);
  555. }
  556. ShellInfoObject.NewEfiShellProtocol->CloseFile (FileHandle);
  557. if (!EFI_ERROR (Status)) {
  558. //
  559. // Get help text from .MAN file success.
  560. //
  561. goto Done;
  562. }
  563. }
  564. //
  565. // Load the app image to check EFI_HII_PACKAGE_LIST_PROTOCOL.
  566. //
  567. CmdFileName = GetExecuatableFileName (TempString);
  568. if (CmdFileName == NULL) {
  569. Status = EFI_OUT_OF_RESOURCES;
  570. goto Done;
  571. }
  572. //
  573. // If the file in CWD then use the file name, else use the full
  574. // path name.
  575. //
  576. CmdFilePathName = ShellFindFilePath (CmdFileName);
  577. if (CmdFilePathName == NULL) {
  578. Status = EFI_NOT_FOUND;
  579. goto Done;
  580. }
  581. DevPath = ShellInfoObject.NewEfiShellProtocol->GetDevicePathFromFilePath (CmdFilePathName);
  582. Status = gBS->LoadImage (FALSE, gImageHandle, DevPath, NULL, 0, &CmdFileImgHandle);
  583. if (EFI_ERROR (Status)) {
  584. //
  585. // With EFI_SECURITY_VIOLATION retval, the Image was loaded and an ImageHandle was created
  586. // with a valid EFI_LOADED_IMAGE_PROTOCOL, but the image can not be started right now.
  587. // If the caller doesn't have the option to defer the execution of an image, we should
  588. // unload image for the EFI_SECURITY_VIOLATION to avoid the resource leak.
  589. //
  590. if (Status == EFI_SECURITY_VIOLATION) {
  591. gBS->UnloadImage (CmdFileImgHandle);
  592. }
  593. *HelpText = NULL;
  594. goto Done;
  595. }
  596. Status = gBS->OpenProtocol (
  597. CmdFileImgHandle,
  598. &gEfiHiiPackageListProtocolGuid,
  599. (VOID **)&PackageListHeader,
  600. gImageHandle,
  601. NULL,
  602. EFI_OPEN_PROTOCOL_GET_PROTOCOL
  603. );
  604. if (EFI_ERROR (Status)) {
  605. *HelpText = NULL;
  606. goto Done;
  607. }
  608. //
  609. // If get package list on image handle, install it on HiiDatabase.
  610. //
  611. Status = gBS->InstallProtocolInterface (
  612. &mShellManDriverHandle,
  613. &gEfiDevicePathProtocolGuid,
  614. EFI_NATIVE_INTERFACE,
  615. &mShellManHiiDevicePath
  616. );
  617. if (EFI_ERROR (Status)) {
  618. goto Done;
  619. }
  620. Status = gHiiDatabase->NewPackageList (
  621. gHiiDatabase,
  622. PackageListHeader,
  623. mShellManDriverHandle,
  624. &mShellManHiiHandle
  625. );
  626. if (EFI_ERROR (Status)) {
  627. goto Done;
  628. }
  629. StringIdWalker = 1;
  630. do {
  631. SHELL_FREE_NON_NULL (TempString);
  632. if (BriefDesc != NULL) {
  633. SHELL_FREE_NON_NULL (*BriefDesc);
  634. }
  635. TempString = HiiGetString (mShellManHiiHandle, (EFI_STRING_ID)StringIdWalker, NULL);
  636. if (TempString == NULL) {
  637. Status = EFI_NOT_FOUND;
  638. goto Done;
  639. }
  640. FileHandle = ConvertEfiFileProtocolToShellHandle (CreateFileInterfaceMem (TRUE), NULL);
  641. HelpSize = StrLen (TempString) * sizeof (CHAR16);
  642. ShellWriteFile (FileHandle, &HelpSize, TempString);
  643. ShellSetFilePosition (FileHandle, 0);
  644. HelpSize = 0;
  645. BriefSize = 0;
  646. Status = ManFileFindTitleSection (FileHandle, Command, BriefDesc, &BriefSize, &Ascii);
  647. if (!EFI_ERROR (Status) && (HelpText != NULL)) {
  648. Status = ManFileFindSections (FileHandle, Sections, HelpText, &HelpSize, Ascii);
  649. }
  650. ShellCloseFile (&FileHandle);
  651. if (!EFI_ERROR (Status)) {
  652. //
  653. // Found what we need and return
  654. //
  655. goto Done;
  656. }
  657. StringIdWalker += 1;
  658. } while (StringIdWalker < 0xFFFF && TempString != NULL);
  659. }
  660. Done:
  661. if (mShellManDriverHandle != NULL) {
  662. gBS->UninstallProtocolInterface (
  663. mShellManDriverHandle,
  664. &gEfiDevicePathProtocolGuid,
  665. &mShellManHiiDevicePath
  666. );
  667. mShellManDriverHandle = NULL;
  668. }
  669. if (mShellManHiiHandle != NULL) {
  670. HiiRemovePackages (mShellManHiiHandle);
  671. mShellManHiiHandle = NULL;
  672. }
  673. if (CmdFileImgHandle != NULL) {
  674. Status = gBS->UnloadImage (CmdFileImgHandle);
  675. }
  676. SHELL_FREE_NON_NULL (TempString);
  677. SHELL_FREE_NON_NULL (CmdFileName);
  678. SHELL_FREE_NON_NULL (CmdFilePathName);
  679. SHELL_FREE_NON_NULL (FileDevPath);
  680. SHELL_FREE_NON_NULL (DevPath);
  681. return (Status);
  682. }