2 Main file for ls shell level 2 function.
4 (C) Copyright 2013-2015 Hewlett-Packard Development Company, L.P.<BR>
5 Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
16 #include "UefiShellLevel2CommandsLib.h"
17 #include <Guid/FileSystemInfo.h>
20 print out the standard format output volume entry.
22 @param[in] TheList a list of files from the volume.
25 PrintSfoVolumeInfoTableEntry(
26 IN CONST EFI_SHELL_FILE_INFO
*TheList
30 EFI_SHELL_FILE_INFO
*Node
;
31 CHAR16
*DirectoryName
;
32 EFI_FILE_SYSTEM_INFO
*SysInfo
;
34 SHELL_FILE_HANDLE ShellFileHandle
;
35 EFI_FILE_PROTOCOL
*EfiFpHandle
;
38 // Get the first valid handle (directories)
40 for ( Node
= (EFI_SHELL_FILE_INFO
*)GetFirstNode(&TheList
->Link
)
41 ; !IsNull(&TheList
->Link
, &Node
->Link
) && Node
->Handle
== NULL
42 ; Node
= (EFI_SHELL_FILE_INFO
*)GetNextNode(&TheList
->Link
, &Node
->Link
)
45 if (Node
->Handle
== NULL
) {
46 DirectoryName
= GetFullyQualifiedPath(((EFI_SHELL_FILE_INFO
*)GetFirstNode(&TheList
->Link
))->FullName
);
49 // We need to open something up to get system information
51 Status
= gEfiShellProtocol
->OpenFileByName(
57 ASSERT_EFI_ERROR(Status
);
58 FreePool(DirectoryName
);
61 // Get the Volume Info from ShellFileHandle
65 EfiFpHandle
= ConvertShellHandleToEfiFileProtocol(ShellFileHandle
);
66 Status
= EfiFpHandle
->GetInfo(
68 &gEfiFileSystemInfoGuid
,
73 if (Status
== EFI_BUFFER_TOO_SMALL
) {
74 SysInfo
= AllocateZeroPool(SysInfoSize
);
75 Status
= EfiFpHandle
->GetInfo(
77 &gEfiFileSystemInfoGuid
,
83 ASSERT_EFI_ERROR(Status
);
85 gEfiShellProtocol
->CloseFile(ShellFileHandle
);
88 // Get the Volume Info from Node->Handle
92 EfiFpHandle
= ConvertShellHandleToEfiFileProtocol(Node
->Handle
);
93 Status
= EfiFpHandle
->GetInfo(
95 &gEfiFileSystemInfoGuid
,
100 if (Status
== EFI_BUFFER_TOO_SMALL
) {
101 SysInfo
= AllocateZeroPool(SysInfoSize
);
102 Status
= EfiFpHandle
->GetInfo(
104 &gEfiFileSystemInfoGuid
,
110 ASSERT_EFI_ERROR(Status
);
117 STRING_TOKEN (STR_GEN_SFO_HEADER
),
118 gShellLevel2HiiHandle
,
122 // print VolumeInfo table
124 ASSERT(SysInfo
!= NULL
);
127 gST
->ConOut
->Mode
->CursorRow
,
129 STRING_TOKEN (STR_LS_SFO_VOLINFO
),
130 gShellLevel2HiiHandle
,
131 SysInfo
->VolumeLabel
,
133 SysInfo
->ReadOnly
?L
"TRUE":L
"FALSE",
138 SHELL_FREE_NON_NULL(SysInfo
);
144 print out the info on a single file.
146 @param[in] Sfo TRUE if in SFO, false otherwise.
147 @param[in] TheNode the EFI_SHELL_FILE_INFO node to print out information on.
148 @param[in] Files incremented if a file is printed.
149 @param[in] Size incremented by file size.
150 @param[in] Dirs incremented if a directory is printed.
154 PrintFileInformation(
155 IN CONST BOOLEAN Sfo
,
156 IN CONST EFI_SHELL_FILE_INFO
*TheNode
,
162 ASSERT(Files
!= NULL
);
163 ASSERT(Size
!= NULL
);
164 ASSERT(Dirs
!= NULL
);
165 ASSERT(TheNode
!= NULL
);
169 // Print the FileInfo Table
173 gST
->ConOut
->Mode
->CursorRow
,
175 STRING_TOKEN (STR_LS_SFO_FILEINFO
),
176 gShellLevel2HiiHandle
,
178 TheNode
->Info
->FileSize
,
179 TheNode
->Info
->PhysicalSize
,
180 (TheNode
->Info
->Attribute
& EFI_FILE_ARCHIVE
) != 0?L
"a":L
"",
181 (TheNode
->Info
->Attribute
& EFI_FILE_DIRECTORY
) != 0?L
"d":L
"",
182 (TheNode
->Info
->Attribute
& EFI_FILE_HIDDEN
) != 0?L
"h":L
"",
183 (TheNode
->Info
->Attribute
& EFI_FILE_READ_ONLY
) != 0?L
"r":L
"",
184 (TheNode
->Info
->Attribute
& EFI_FILE_SYSTEM
) != 0?L
"s":L
"",
185 TheNode
->Info
->CreateTime
.Hour
,
186 TheNode
->Info
->CreateTime
.Minute
,
187 TheNode
->Info
->CreateTime
.Second
,
188 TheNode
->Info
->CreateTime
.Day
,
189 TheNode
->Info
->CreateTime
.Month
,
190 TheNode
->Info
->CreateTime
.Year
,
191 TheNode
->Info
->LastAccessTime
.Hour
,
192 TheNode
->Info
->LastAccessTime
.Minute
,
193 TheNode
->Info
->LastAccessTime
.Second
,
194 TheNode
->Info
->LastAccessTime
.Day
,
195 TheNode
->Info
->LastAccessTime
.Month
,
196 TheNode
->Info
->LastAccessTime
.Year
,
197 TheNode
->Info
->ModificationTime
.Hour
,
198 TheNode
->Info
->ModificationTime
.Minute
,
199 TheNode
->Info
->ModificationTime
.Second
,
200 TheNode
->Info
->ModificationTime
.Day
,
201 TheNode
->Info
->ModificationTime
.Month
,
202 TheNode
->Info
->ModificationTime
.Year
206 // print this one out...
207 // first print the universal start, next print the type specific name format, last print the CRLF
213 STRING_TOKEN (STR_LS_LINE_START_ALL
),
214 gShellLevel2HiiHandle
,
215 &TheNode
->Info
->ModificationTime
,
216 (TheNode
->Info
->Attribute
& EFI_FILE_DIRECTORY
) != 0?L
"<DIR>":L
"",
217 (TheNode
->Info
->Attribute
& EFI_FILE_READ_ONLY
) != 0?L
'r':L
' ',
218 TheNode
->Info
->FileSize
220 if (TheNode
->Info
->Attribute
& EFI_FILE_DIRECTORY
) {
226 STRING_TOKEN (STR_LS_LINE_END_DIR
),
227 gShellLevel2HiiHandle
,
232 (*Size
) += TheNode
->Info
->FileSize
;
233 if ( (gUnicodeCollation
->StriColl(gUnicodeCollation
, (CHAR16
*)L
".nsh", (CHAR16
*)&(TheNode
->FileName
[StrLen (TheNode
->FileName
) - 4])) == 0)
234 || (gUnicodeCollation
->StriColl(gUnicodeCollation
, (CHAR16
*)L
".efi", (CHAR16
*)&(TheNode
->FileName
[StrLen (TheNode
->FileName
) - 4])) == 0)
240 STRING_TOKEN (STR_LS_LINE_END_EXE
),
241 gShellLevel2HiiHandle
,
249 STRING_TOKEN (STR_LS_LINE_END_FILE
),
250 gShellLevel2HiiHandle
,
259 print out the header when not using standard format output.
261 @param[in] Path String with starting path.
265 IN CONST CHAR16
*Path
268 CHAR16
*DirectoryName
;
271 // get directory name from path...
273 DirectoryName
= GetFullyQualifiedPath(Path
);
275 if (DirectoryName
!= NULL
) {
281 gST
->ConOut
->Mode
->CursorRow
,
283 STRING_TOKEN (STR_LS_HEADER_LINE1
),
284 gShellLevel2HiiHandle
,
288 SHELL_FREE_NON_NULL(DirectoryName
);
293 print out the footer when not using standard format output.
295 @param[in] Files The number of files.
296 @param[in] Size The size of files in bytes.
297 @param[in] Dirs The number of directories.
313 STRING_TOKEN (STR_LS_FOOTER_LINE
),
314 gShellLevel2HiiHandle
,
322 print out the list of files and directories from the LS command
324 @param[in] Rec TRUE to automatically recurse into each found directory
325 FALSE to only list the specified directory.
326 @param[in] Attribs List of required Attribute for display.
327 If 0 then all non-system and non-hidden files will be printed.
328 @param[in] Sfo TRUE to use Standard Format Output, FALSE otherwise
329 @param[in] RootPath String with starting path to search in.
330 @param[in] SearchString String with search string.
331 @param[in] Found Set to TRUE, if anyone were found.
332 @param[in] Count The count of bits enabled in Attribs.
333 @param[in] TimeZone The current time zone offset.
335 @retval SHELL_SUCCESS the printing was sucessful.
339 IN CONST BOOLEAN Rec
,
340 IN CONST UINT64 Attribs
,
341 IN CONST BOOLEAN Sfo
,
342 IN CONST CHAR16
*RootPath
,
343 IN CONST CHAR16
*SearchString
,
345 IN CONST UINTN Count
,
346 IN CONST INT16 TimeZone
350 EFI_SHELL_FILE_INFO
*ListHead
;
351 EFI_SHELL_FILE_INFO
*Node
;
352 SHELL_STATUS ShellStatus
;
357 CHAR16
*CorrectedPath
;
359 BOOLEAN HeaderPrinted
;
361 HeaderPrinted
= FALSE
;
366 ShellStatus
= SHELL_SUCCESS
;
368 CorrectedPath
= NULL
;
376 CorrectedPath
= StrnCatGrow(&CorrectedPath
, &LongestPath
, RootPath
, 0);
377 if (CorrectedPath
== NULL
) {
378 return SHELL_OUT_OF_RESOURCES
;
380 if (CorrectedPath
[StrLen(CorrectedPath
)-1] != L
'\\'
381 &&CorrectedPath
[StrLen(CorrectedPath
)-1] != L
'/') {
382 CorrectedPath
= StrnCatGrow(&CorrectedPath
, &LongestPath
, L
"\\", 0);
384 CorrectedPath
= StrnCatGrow(&CorrectedPath
, &LongestPath
, SearchString
, 0);
385 if (CorrectedPath
== NULL
) {
386 return (SHELL_OUT_OF_RESOURCES
);
389 PathCleanUpDirectories(CorrectedPath
);
391 Status
= ShellOpenFileMetaArg((CHAR16
*)CorrectedPath
, EFI_FILE_MODE_READ
, &ListHead
);
392 if (!EFI_ERROR(Status
)) {
393 if (ListHead
== NULL
|| IsListEmpty(&ListHead
->Link
)) {
394 SHELL_FREE_NON_NULL(CorrectedPath
);
395 return (SHELL_SUCCESS
);
398 if (Sfo
&& Found
== NULL
) {
399 PrintSfoVolumeInfoTableEntry(ListHead
);
402 for ( Node
= (EFI_SHELL_FILE_INFO
*)GetFirstNode(&ListHead
->Link
), LongestPath
= 0
403 ; !IsNull(&ListHead
->Link
, &Node
->Link
)
404 ; Node
= (EFI_SHELL_FILE_INFO
*)GetNextNode(&ListHead
->Link
, &Node
->Link
)
406 if (ShellGetExecutionBreakFlag ()) {
407 ShellStatus
= SHELL_ABORTED
;
410 ASSERT(Node
!= NULL
);
411 if (LongestPath
< StrSize(Node
->FullName
)) {
412 LongestPath
= StrSize(Node
->FullName
);
414 ASSERT(Node
->Info
!= NULL
);
415 ASSERT((Node
->Info
->Attribute
& EFI_FILE_VALID_ATTR
) == Node
->Info
->Attribute
);
418 // NOT system & NOT hidden
420 if ( (Node
->Info
->Attribute
& EFI_FILE_SYSTEM
)
421 || (Node
->Info
->Attribute
& EFI_FILE_HIDDEN
)
425 } else if ((Attribs
!= EFI_FILE_VALID_ATTR
) ||
428 // Only matches the bits which "Attribs" contains, not
429 // all files/directories with any of the bits.
430 // Count == 5 is used to tell the difference between a user
431 // specifying all bits (EX: -arhsda) and just specifying
432 // -a (means display all files with any attribute).
434 if ( (Node
->Info
->Attribute
& Attribs
) != Attribs
) {
439 if (!Sfo
&& !HeaderPrinted
) {
440 PathRemoveLastItem (CorrectedPath
);
441 PrintNonSfoHeader(CorrectedPath
);
443 PrintFileInformation(Sfo
, Node
, &FileCount
, &FileSize
, &DirCount
);
445 HeaderPrinted
= TRUE
;
448 if (!Sfo
&& ShellStatus
!= SHELL_ABORTED
) {
449 PrintNonSfoFooter(FileCount
, FileSize
, DirCount
);
453 if (Rec
&& ShellStatus
!= SHELL_ABORTED
) {
455 // Re-Open all the files under the starting path for directories that didnt necessarily match our file filter
457 ShellCloseFileMetaArg(&ListHead
);
458 CorrectedPath
[0] = CHAR_NULL
;
459 CorrectedPath
= StrnCatGrow(&CorrectedPath
, &LongestPath
, RootPath
, 0);
460 if (CorrectedPath
== NULL
) {
461 return SHELL_OUT_OF_RESOURCES
;
463 if (CorrectedPath
[StrLen(CorrectedPath
)-1] != L
'\\'
464 &&CorrectedPath
[StrLen(CorrectedPath
)-1] != L
'/') {
465 CorrectedPath
= StrnCatGrow(&CorrectedPath
, &LongestPath
, L
"\\", 0);
467 CorrectedPath
= StrnCatGrow(&CorrectedPath
, &LongestPath
, L
"*", 0);
468 Status
= ShellOpenFileMetaArg((CHAR16
*)CorrectedPath
, EFI_FILE_MODE_READ
, &ListHead
);
470 if (!EFI_ERROR(Status
)) {
471 for ( Node
= (EFI_SHELL_FILE_INFO
*)GetFirstNode(&ListHead
->Link
)
472 ; !IsNull(&ListHead
->Link
, &Node
->Link
) && ShellStatus
== SHELL_SUCCESS
473 ; Node
= (EFI_SHELL_FILE_INFO
*)GetNextNode(&ListHead
->Link
, &Node
->Link
)
475 if (ShellGetExecutionBreakFlag ()) {
476 ShellStatus
= SHELL_ABORTED
;
481 // recurse on any directory except the traversing ones...
483 if (((Node
->Info
->Attribute
& EFI_FILE_DIRECTORY
) == EFI_FILE_DIRECTORY
)
484 && StrCmp(Node
->FileName
, L
".") != 0
485 && StrCmp(Node
->FileName
, L
"..") != 0
487 ShellStatus
= PrintLsOutput(
498 // Since it's running recursively, we have to break immediately when returned SHELL_ABORTED
500 if (ShellStatus
== SHELL_ABORTED
) {
508 SHELL_FREE_NON_NULL(CorrectedPath
);
509 ShellCloseFileMetaArg(&ListHead
);
511 if (Found
== NULL
&& !FoundOne
) {
512 return (SHELL_NOT_FOUND
);
519 return (ShellStatus
);
522 STATIC CONST SHELL_PARAM_ITEM LsParamList
[] = {
530 Function for 'ls' command.
532 @param[in] ImageHandle Handle to the Image (NULL if Internal).
533 @param[in] SystemTable Pointer to the System Table (NULL if Internal).
538 IN EFI_HANDLE ImageHandle
,
539 IN EFI_SYSTEM_TABLE
*SystemTable
544 CHAR16
*ProblemParam
;
545 CONST CHAR16
*Attribs
;
546 SHELL_STATUS ShellStatus
;
547 UINT64 RequiredAttributes
;
548 CONST CHAR16
*PathName
;
549 CONST CHAR16
*CurDir
;
554 CHAR16
*SearchString
;
560 ShellStatus
= SHELL_SUCCESS
;
561 RequiredAttributes
= 0;
568 // initialize the shell lib (we must be in non-auto-init...)
570 Status
= ShellInitialize();
571 ASSERT_EFI_ERROR(Status
);
574 // Fix local copies of the protocol pointers
576 Status
= CommandInit();
577 ASSERT_EFI_ERROR(Status
);
580 // parse the command line
582 Status
= ShellCommandLineParse (LsParamList
, &Package
, &ProblemParam
, TRUE
);
583 if (EFI_ERROR(Status
)) {
584 if (Status
== EFI_VOLUME_CORRUPTED
&& ProblemParam
!= NULL
) {
585 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_GEN_PROBLEM
), gShellLevel2HiiHandle
, L
"ls", ProblemParam
);
586 FreePool(ProblemParam
);
587 ShellStatus
= SHELL_INVALID_PARAMETER
;
595 if (ShellCommandLineGetFlag(Package
, L
"-?")) {
599 if (ShellCommandLineGetCount(Package
) > 2) {
600 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_GEN_TOO_MANY
), gShellLevel2HiiHandle
, L
"ls");
601 ShellStatus
= SHELL_INVALID_PARAMETER
;
606 if (ShellCommandLineGetFlag(Package
, L
"-a")) {
607 for ( Attribs
= ShellCommandLineGetValue(Package
, L
"-a")
608 ; Attribs
!= NULL
&& *Attribs
!= CHAR_NULL
&& ShellStatus
== SHELL_SUCCESS
614 RequiredAttributes
|= EFI_FILE_ARCHIVE
;
619 RequiredAttributes
|= EFI_FILE_SYSTEM
;
624 RequiredAttributes
|= EFI_FILE_HIDDEN
;
629 RequiredAttributes
|= EFI_FILE_READ_ONLY
;
634 RequiredAttributes
|= EFI_FILE_DIRECTORY
;
638 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_GEN_ATTRIBUTE
), gShellLevel2HiiHandle
, L
"ls", ShellCommandLineGetValue(Package
, L
"-a"));
639 ShellStatus
= SHELL_INVALID_PARAMETER
;
644 // if nothing is specified all are specified
646 if (RequiredAttributes
== 0) {
647 RequiredAttributes
= EFI_FILE_VALID_ATTR
;
650 if (ShellStatus
== SHELL_SUCCESS
) {
651 PathName
= ShellCommandLineGetRawValue(Package
, 1);
652 if (PathName
== NULL
) {
654 // Nothing specified... must start from current directory
656 CurDir
= gEfiShellProtocol
->GetCurDir(NULL
);
657 if (CurDir
== NULL
) {
658 ShellStatus
= SHELL_NOT_FOUND
;
659 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_GEN_NO_CWD
), gShellLevel2HiiHandle
, L
"ls");
662 // Copy to the 2 strings for starting path and file search string
664 ASSERT(SearchString
== NULL
);
665 ASSERT(FullPath
== NULL
);
666 StrnCatGrow(&SearchString
, NULL
, L
"*", 0);
667 StrnCatGrow(&FullPath
, NULL
, CurDir
, 0);
668 Size
= FullPath
!= NULL
? StrSize(FullPath
) : 0;
669 StrnCatGrow(&FullPath
, &Size
, L
"\\", 0);
671 if (StrStr(PathName
, L
":") == NULL
&& gEfiShellProtocol
->GetCurDir(NULL
) == NULL
) {
673 // If we got something and it doesnt have a fully qualified path, then we needed to have a CWD.
675 ShellStatus
= SHELL_NOT_FOUND
;
676 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_GEN_NO_CWD
), gShellLevel2HiiHandle
, L
"ls");
679 // We got a valid fully qualified path or we have a CWD
681 ASSERT((FullPath
== NULL
&& Size
== 0) || (FullPath
!= NULL
));
682 if (StrStr(PathName
, L
":") == NULL
) {
683 StrnCatGrow(&FullPath
, &Size
, gEfiShellProtocol
->GetCurDir(NULL
), 0);
684 if (FullPath
== NULL
) {
685 ShellCommandLineFreeVarList (Package
);
686 return SHELL_OUT_OF_RESOURCES
;
688 Size
= FullPath
!= NULL
? StrSize(FullPath
) : 0;
689 StrnCatGrow(&FullPath
, &Size
, L
"\\", 0);
691 StrnCatGrow(&FullPath
, &Size
, PathName
, 0);
692 if (FullPath
== NULL
) {
693 ShellCommandLineFreeVarList (Package
);
694 return SHELL_OUT_OF_RESOURCES
;
697 if (ShellIsDirectory(PathName
) == EFI_SUCCESS
) {
699 // is listing ends with a directory, then we list all files in that directory
701 StrnCatGrow(&SearchString
, NULL
, L
"*", 0);
704 // must split off the search part that applies to files from the end of the directory part
706 StrnCatGrow(&SearchString
, NULL
, FullPath
, 0);
707 if (SearchString
== NULL
) {
709 ShellCommandLineFreeVarList (Package
);
710 return SHELL_OUT_OF_RESOURCES
;
712 PathRemoveLastItem (FullPath
);
713 CopyMem (SearchString
, SearchString
+ StrLen (FullPath
), StrSize (SearchString
+ StrLen (FullPath
)));
717 Status
= gRT
->GetTime(&TheTime
, NULL
);
718 if (EFI_ERROR(Status
)) {
719 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_GEN_UEFI_FUNC_WARN
), gShellLevel2HiiHandle
, L
"ls", L
"gRT->GetTime", Status
);
720 TheTime
.TimeZone
= EFI_UNSPECIFIED_TIMEZONE
;
723 if (ShellStatus
== SHELL_SUCCESS
) {
724 ShellStatus
= PrintLsOutput(
725 ShellCommandLineGetFlag(Package
, L
"-r"),
727 ShellCommandLineGetFlag(Package
, L
"-sfo"),
734 if (ShellStatus
== SHELL_NOT_FOUND
) {
735 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_LS_FILE_NOT_FOUND
), gShellLevel2HiiHandle
, L
"ls", FullPath
);
736 } else if (ShellStatus
== SHELL_INVALID_PARAMETER
) {
737 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_GEN_PARAM_INV
), gShellLevel2HiiHandle
, L
"ls", FullPath
);
738 } else if (ShellStatus
== SHELL_ABORTED
) {
742 } else if (ShellStatus
!= SHELL_SUCCESS
) {
743 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_GEN_PARAM_INV
), gShellLevel2HiiHandle
, L
"ls", FullPath
);
751 // Free memory allocated
753 SHELL_FREE_NON_NULL(SearchString
);
754 SHELL_FREE_NON_NULL(FullPath
);
755 ShellCommandLineFreeVarList (Package
);
757 return (ShellStatus
);