1 /** @file
2   Main file for ls shell level 2 function.
3 
4   (C) Copyright 2013-2015 Hewlett-Packard Development Company, L.P.<BR>
5   Copyright (c) 2009 - 2015, 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
10 
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.
13 
14 **/
15 
16 #include "UefiShellLevel2CommandsLib.h"
17 #include <Guid/FileSystemInfo.h>
18 
19 /**
20   print out the standard format output volume entry.
21 
22   @param[in] TheList           a list of files from the volume.
23 **/
24 EFI_STATUS
25 EFIAPI
PrintSfoVolumeInfoTableEntry(IN CONST EFI_SHELL_FILE_INFO * TheList)26 PrintSfoVolumeInfoTableEntry(
27   IN CONST EFI_SHELL_FILE_INFO *TheList
28   )
29 {
30   EFI_STATUS            Status;
31   EFI_SHELL_FILE_INFO   *Node;
32   CHAR16                *DirectoryName;
33   EFI_FILE_SYSTEM_INFO  *SysInfo;
34   UINTN                 SysInfoSize;
35   SHELL_FILE_HANDLE     ShellFileHandle;
36   EFI_FILE_PROTOCOL     *EfiFpHandle;
37 
38   //
39   // Get the first valid handle (directories)
40   //
41   for ( Node = (EFI_SHELL_FILE_INFO *)GetFirstNode(&TheList->Link)
42       ; !IsNull(&TheList->Link, &Node->Link) && Node->Handle == NULL
43       ; Node = (EFI_SHELL_FILE_INFO *)GetNextNode(&TheList->Link, &Node->Link)
44      );
45 
46   if (Node->Handle == NULL) {
47     DirectoryName = GetFullyQualifiedPath(((EFI_SHELL_FILE_INFO *)GetFirstNode(&TheList->Link))->FullName);
48 
49     //
50     // We need to open something up to get system information
51     //
52     Status = gEfiShellProtocol->OpenFileByName(
53       DirectoryName,
54       &ShellFileHandle,
55       EFI_FILE_MODE_READ
56       );
57 
58     ASSERT_EFI_ERROR(Status);
59     FreePool(DirectoryName);
60 
61     //
62     // Get the Volume Info from ShellFileHandle
63     //
64     SysInfo     = NULL;
65     SysInfoSize = 0;
66     EfiFpHandle = ConvertShellHandleToEfiFileProtocol(ShellFileHandle);
67     Status = EfiFpHandle->GetInfo(
68       EfiFpHandle,
69       &gEfiFileSystemInfoGuid,
70       &SysInfoSize,
71       SysInfo
72       );
73 
74     if (Status == EFI_BUFFER_TOO_SMALL) {
75       SysInfo = AllocateZeroPool(SysInfoSize);
76       Status = EfiFpHandle->GetInfo(
77         EfiFpHandle,
78         &gEfiFileSystemInfoGuid,
79         &SysInfoSize,
80         SysInfo
81         );
82     }
83 
84     ASSERT_EFI_ERROR(Status);
85 
86     gEfiShellProtocol->CloseFile(ShellFileHandle);
87   } else {
88     //
89     // Get the Volume Info from Node->Handle
90     //
91     SysInfo = NULL;
92     SysInfoSize = 0;
93     EfiFpHandle = ConvertShellHandleToEfiFileProtocol(Node->Handle);
94     Status = EfiFpHandle->GetInfo(
95       EfiFpHandle,
96       &gEfiFileSystemInfoGuid,
97       &SysInfoSize,
98       SysInfo
99       );
100 
101     if (Status == EFI_BUFFER_TOO_SMALL) {
102       SysInfo = AllocateZeroPool(SysInfoSize);
103       Status = EfiFpHandle->GetInfo(
104         EfiFpHandle,
105         &gEfiFileSystemInfoGuid,
106         &SysInfoSize,
107         SysInfo
108         );
109     }
110 
111     ASSERT_EFI_ERROR(Status);
112   }
113 
114   ShellPrintHiiEx (
115     -1,
116     -1,
117     NULL,
118     STRING_TOKEN (STR_GEN_SFO_HEADER),
119     gShellLevel2HiiHandle,
120     L"ls"
121     );
122   //
123   // print VolumeInfo table
124   //
125   ASSERT(SysInfo != NULL);
126   ShellPrintHiiEx (
127     0,
128     gST->ConOut->Mode->CursorRow,
129     NULL,
130     STRING_TOKEN (STR_LS_SFO_VOLINFO),
131     gShellLevel2HiiHandle,
132     SysInfo->VolumeLabel,
133     SysInfo->VolumeSize,
134     SysInfo->ReadOnly?L"TRUE":L"FALSE",
135     SysInfo->FreeSpace,
136     SysInfo->BlockSize
137     );
138 
139   SHELL_FREE_NON_NULL(SysInfo);
140 
141   return (Status);
142 }
143 
144 /**
145   print out the info on a single file.
146 
147   @param[in] Sfo      TRUE if in SFO, false otherwise.
148   @param[in] TheNode  the EFI_SHELL_FILE_INFO node to print out information on.
149   @param[in] Files    incremented if a file is printed.
150   @param[in] Size     incremented by file size.
151   @param[in] Dirs     incremented if a directory is printed.
152 
153 **/
154 VOID
155 EFIAPI
PrintFileInformation(IN CONST BOOLEAN Sfo,IN CONST EFI_SHELL_FILE_INFO * TheNode,IN UINT64 * Files,IN UINT64 * Size,IN UINT64 * Dirs)156 PrintFileInformation(
157   IN CONST BOOLEAN              Sfo,
158   IN CONST EFI_SHELL_FILE_INFO  *TheNode,
159   IN UINT64                     *Files,
160   IN UINT64                     *Size,
161   IN UINT64                     *Dirs
162   )
163 {
164   ASSERT(Files    != NULL);
165   ASSERT(Size     != NULL);
166   ASSERT(Dirs     != NULL);
167   ASSERT(TheNode  != NULL);
168 
169   if (Sfo) {
170     //
171     // Print the FileInfo Table
172     //
173     ShellPrintHiiEx (
174       0,
175       gST->ConOut->Mode->CursorRow,
176       NULL,
177       STRING_TOKEN (STR_LS_SFO_FILEINFO),
178       gShellLevel2HiiHandle,
179       TheNode->FullName,
180       TheNode->Info->FileSize,
181       TheNode->Info->PhysicalSize,
182       (TheNode->Info->Attribute & EFI_FILE_ARCHIVE)   != 0?L"a":L"",
183       (TheNode->Info->Attribute & EFI_FILE_DIRECTORY) != 0?L"d":L"",
184       (TheNode->Info->Attribute & EFI_FILE_HIDDEN)    != 0?L"h":L"",
185       (TheNode->Info->Attribute & EFI_FILE_READ_ONLY) != 0?L"r":L"",
186       (TheNode->Info->Attribute & EFI_FILE_SYSTEM)    != 0?L"s":L"",
187       TheNode->Info->CreateTime.Hour,
188       TheNode->Info->CreateTime.Minute,
189       TheNode->Info->CreateTime.Second,
190       TheNode->Info->CreateTime.Day,
191       TheNode->Info->CreateTime.Month,
192       TheNode->Info->CreateTime.Year,
193       TheNode->Info->LastAccessTime.Hour,
194       TheNode->Info->LastAccessTime.Minute,
195       TheNode->Info->LastAccessTime.Second,
196       TheNode->Info->LastAccessTime.Day,
197       TheNode->Info->LastAccessTime.Month,
198       TheNode->Info->LastAccessTime.Year,
199       TheNode->Info->ModificationTime.Hour,
200       TheNode->Info->ModificationTime.Minute,
201       TheNode->Info->ModificationTime.Second,
202       TheNode->Info->ModificationTime.Day,
203       TheNode->Info->ModificationTime.Month,
204       TheNode->Info->ModificationTime.Year
205       );
206   } else {
207     //
208     // print this one out...
209     // first print the universal start, next print the type specific name format, last print the CRLF
210     //
211     ShellPrintHiiEx (
212       -1,
213       -1,
214       NULL,
215       STRING_TOKEN (STR_LS_LINE_START_ALL),
216       gShellLevel2HiiHandle,
217       &TheNode->Info->ModificationTime,
218       (TheNode->Info->Attribute & EFI_FILE_DIRECTORY) != 0?L"<DIR>":L"",
219       (TheNode->Info->Attribute & EFI_FILE_READ_ONLY) != 0?L'r':L' ',
220       TheNode->Info->FileSize
221       );
222     if (TheNode->Info->Attribute & EFI_FILE_DIRECTORY) {
223       (*Dirs)++;
224       ShellPrintHiiEx (
225         -1,
226         -1,
227         NULL,
228         STRING_TOKEN (STR_LS_LINE_END_DIR),
229         gShellLevel2HiiHandle,
230         TheNode->FileName
231         );
232     } else {
233       (*Files)++;
234       (*Size) += TheNode->Info->FileSize;
235       if ( (gUnicodeCollation->StriColl(gUnicodeCollation, (CHAR16*)L".nsh", (CHAR16*)&(TheNode->FileName[StrLen (TheNode->FileName) - 4])) == 0)
236         || (gUnicodeCollation->StriColl(gUnicodeCollation, (CHAR16*)L".efi", (CHAR16*)&(TheNode->FileName[StrLen (TheNode->FileName) - 4])) == 0)
237        ){
238         ShellPrintHiiEx (
239           -1,
240           -1,
241           NULL,
242           STRING_TOKEN (STR_LS_LINE_END_EXE),
243           gShellLevel2HiiHandle,
244           TheNode->FileName
245           );
246       } else {
247         ShellPrintHiiEx (
248           -1,
249           -1,
250           NULL,
251           STRING_TOKEN (STR_LS_LINE_END_FILE),
252           gShellLevel2HiiHandle,
253           TheNode->FileName
254           );
255       }
256     }
257   }
258 }
259 
260 /**
261   print out the header when not using standard format output.
262 
263   @param[in] Path           String with starting path.
264 **/
265 VOID
266 EFIAPI
PrintNonSfoHeader(IN CONST CHAR16 * Path)267 PrintNonSfoHeader(
268   IN CONST CHAR16 *Path
269   )
270 {
271   CHAR16 *DirectoryName;
272 
273   //
274   // get directory name from path...
275   //
276   DirectoryName = GetFullyQualifiedPath(Path);
277 
278   if (DirectoryName != NULL) {
279     //
280     // print header
281     //
282     ShellPrintHiiEx (
283       0,
284       gST->ConOut->Mode->CursorRow,
285       NULL,
286       STRING_TOKEN (STR_LS_HEADER_LINE1),
287       gShellLevel2HiiHandle,
288       DirectoryName
289       );
290 
291     SHELL_FREE_NON_NULL(DirectoryName);
292   }
293 }
294 
295 /**
296   print out the footer when not using standard format output.
297 
298   @param[in] Files            The number of files.
299   @param[in] Size             The size of files in bytes.
300   @param[in] Dirs             The number of directories.
301 **/
302 VOID
303 EFIAPI
PrintNonSfoFooter(IN UINT64 Files,IN UINT64 Size,IN UINT64 Dirs)304 PrintNonSfoFooter(
305   IN UINT64                     Files,
306   IN UINT64                     Size,
307   IN UINT64                     Dirs
308   )
309 {
310   //
311   // print footer
312   //
313   ShellPrintHiiEx (
314     -1,
315     -1,
316     NULL,
317     STRING_TOKEN (STR_LS_FOOTER_LINE),
318     gShellLevel2HiiHandle,
319     Files,
320     Size,
321     Dirs
322    );
323 }
324 
325 /**
326   print out the list of files and directories from the LS command
327 
328   @param[in] Rec            TRUE to automatically recurse into each found directory
329                             FALSE to only list the specified directory.
330   @param[in] Attribs        List of required Attribute for display.
331                             If 0 then all non-system and non-hidden files will be printed.
332   @param[in] Sfo            TRUE to use Standard Format Output, FALSE otherwise
333   @param[in] RootPath       String with starting path to search in.
334   @param[in] SearchString   String with search string.
335   @param[in] Found          Set to TRUE, if anyone were found.
336   @param[in] Count          The count of bits enabled in Attribs.
337   @param[in] TimeZone       The current time zone offset.
338 
339   @retval SHELL_SUCCESS     the printing was sucessful.
340 **/
341 SHELL_STATUS
342 EFIAPI
PrintLsOutput(IN CONST BOOLEAN Rec,IN CONST UINT64 Attribs,IN CONST BOOLEAN Sfo,IN CONST CHAR16 * RootPath,IN CONST CHAR16 * SearchString,IN BOOLEAN * Found,IN CONST UINTN Count,IN CONST INT16 TimeZone)343 PrintLsOutput(
344   IN CONST BOOLEAN Rec,
345   IN CONST UINT64  Attribs,
346   IN CONST BOOLEAN Sfo,
347   IN CONST CHAR16  *RootPath,
348   IN CONST CHAR16  *SearchString,
349   IN       BOOLEAN *Found,
350   IN CONST UINTN   Count,
351   IN CONST INT16   TimeZone
352   )
353 {
354   EFI_STATUS            Status;
355   EFI_SHELL_FILE_INFO   *ListHead;
356   EFI_SHELL_FILE_INFO   *Node;
357   SHELL_STATUS          ShellStatus;
358   UINT64                FileCount;
359   UINT64                DirCount;
360   UINT64                FileSize;
361   UINTN                 LongestPath;
362   CHAR16                *CorrectedPath;
363   BOOLEAN               FoundOne;
364   BOOLEAN               HeaderPrinted;
365 
366   HeaderPrinted = FALSE;
367   FileCount     = 0;
368   DirCount      = 0;
369   FileSize      = 0;
370   ListHead      = NULL;
371   ShellStatus   = SHELL_SUCCESS;
372   LongestPath   = 0;
373   CorrectedPath = NULL;
374 
375   if (Found != NULL) {
376     FoundOne = *Found;
377   } else {
378     FoundOne = FALSE;
379   }
380 
381   CorrectedPath = StrnCatGrow(&CorrectedPath, &LongestPath, RootPath,     0);
382   if (CorrectedPath == NULL) {
383     return SHELL_OUT_OF_RESOURCES;
384   }
385   if (CorrectedPath[StrLen(CorrectedPath)-1] != L'\\'
386     &&CorrectedPath[StrLen(CorrectedPath)-1] != L'/') {
387     CorrectedPath = StrnCatGrow(&CorrectedPath, &LongestPath, L"\\",     0);
388   }
389   CorrectedPath = StrnCatGrow(&CorrectedPath, &LongestPath, SearchString, 0);
390   if (CorrectedPath == NULL) {
391     return (SHELL_OUT_OF_RESOURCES);
392   }
393 
394   PathCleanUpDirectories(CorrectedPath);
395 
396   Status = ShellOpenFileMetaArg((CHAR16*)CorrectedPath, EFI_FILE_MODE_READ, &ListHead);
397   if (!EFI_ERROR(Status)) {
398     if (ListHead == NULL || IsListEmpty(&ListHead->Link)) {
399       SHELL_FREE_NON_NULL(CorrectedPath);
400       return (SHELL_SUCCESS);
401     }
402 
403     if (Sfo && Found == NULL) {
404       PrintSfoVolumeInfoTableEntry(ListHead);
405     }
406 
407     for ( Node = (EFI_SHELL_FILE_INFO *)GetFirstNode(&ListHead->Link), LongestPath = 0
408         ; !IsNull(&ListHead->Link, &Node->Link)
409         ; Node = (EFI_SHELL_FILE_INFO *)GetNextNode(&ListHead->Link, &Node->Link)
410         ){
411       if (ShellGetExecutionBreakFlag ()) {
412         ShellStatus = SHELL_ABORTED;
413         break;
414       }
415       ASSERT(Node != NULL);
416       if (LongestPath < StrSize(Node->FullName)) {
417         LongestPath = StrSize(Node->FullName);
418       }
419       ASSERT(Node->Info != NULL);
420       ASSERT((Node->Info->Attribute & EFI_FILE_VALID_ATTR) == Node->Info->Attribute);
421       if (Attribs == 0) {
422         //
423         // NOT system & NOT hidden
424         //
425         if ( (Node->Info->Attribute & EFI_FILE_SYSTEM)
426           || (Node->Info->Attribute & EFI_FILE_HIDDEN)
427          ){
428           continue;
429         }
430       } else if ((Attribs != EFI_FILE_VALID_ATTR) ||
431                  (Count == 5)) {
432         //
433         // Only matches the bits which "Attribs" contains, not
434         // all files/directories with any of the bits.
435         // Count == 5 is used to tell the difference between a user
436         // specifying all bits (EX: -arhsda) and just specifying
437         // -a (means display all files with any attribute).
438         //
439         if ( (Node->Info->Attribute & Attribs) != Attribs) {
440           continue;
441         }
442       }
443 
444       if (!Sfo && !HeaderPrinted) {
445         PrintNonSfoHeader(CorrectedPath);
446       }
447       PrintFileInformation(Sfo, Node, &FileCount, &FileSize, &DirCount);
448       FoundOne = TRUE;
449       HeaderPrinted = TRUE;
450     }
451 
452     if (!Sfo && ShellStatus != SHELL_ABORTED) {
453       PrintNonSfoFooter(FileCount, FileSize, DirCount);
454     }
455   }
456 
457   if (Rec && ShellStatus != SHELL_ABORTED) {
458     //
459     // Re-Open all the files under the starting path for directories that didnt necessarily match our file filter
460     //
461     ShellCloseFileMetaArg(&ListHead);
462     CorrectedPath[0] = CHAR_NULL;
463     CorrectedPath = StrnCatGrow(&CorrectedPath, &LongestPath, RootPath, 0);
464     if (CorrectedPath == NULL) {
465       return SHELL_OUT_OF_RESOURCES;
466     }
467     if (CorrectedPath[StrLen(CorrectedPath)-1] != L'\\'
468       &&CorrectedPath[StrLen(CorrectedPath)-1] != L'/') {
469       CorrectedPath = StrnCatGrow(&CorrectedPath, &LongestPath, L"\\",     0);
470     }
471     CorrectedPath = StrnCatGrow(&CorrectedPath, &LongestPath, L"*",     0);
472     Status = ShellOpenFileMetaArg((CHAR16*)CorrectedPath, EFI_FILE_MODE_READ, &ListHead);
473 
474     if (!EFI_ERROR(Status)) {
475       for ( Node = (EFI_SHELL_FILE_INFO *)GetFirstNode(&ListHead->Link)
476           ; !IsNull(&ListHead->Link, &Node->Link) && ShellStatus == SHELL_SUCCESS
477           ; Node = (EFI_SHELL_FILE_INFO *)GetNextNode(&ListHead->Link, &Node->Link)
478          ){
479         if (ShellGetExecutionBreakFlag ()) {
480           ShellStatus = SHELL_ABORTED;
481           break;
482         }
483 
484         //
485         // recurse on any directory except the traversing ones...
486         //
487         if (((Node->Info->Attribute & EFI_FILE_DIRECTORY) == EFI_FILE_DIRECTORY)
488           && StrCmp(Node->FileName, L".") != 0
489           && StrCmp(Node->FileName, L"..") != 0
490          ){
491           ShellStatus = PrintLsOutput(
492             Rec,
493             Attribs,
494             Sfo,
495             Node->FullName,
496             SearchString,
497             &FoundOne,
498             Count,
499             TimeZone);
500 
501           //
502           // Since it's running recursively, we have to break immediately when returned SHELL_ABORTED
503           //
504           if (ShellStatus == SHELL_ABORTED) {
505             break;
506           }
507         }
508       }
509     }
510   }
511 
512   SHELL_FREE_NON_NULL(CorrectedPath);
513   ShellCloseFileMetaArg(&ListHead);
514 
515   if (Found == NULL && !FoundOne) {
516     return (SHELL_NOT_FOUND);
517   }
518 
519   if (Found != NULL) {
520     *Found = FoundOne;
521   }
522 
523   return (ShellStatus);
524 }
525 
526 STATIC CONST SHELL_PARAM_ITEM LsParamList[] = {
527   {L"-r", TypeFlag},
528   {L"-a", TypeStart},
529   {L"-sfo", TypeFlag},
530   {NULL, TypeMax}
531   };
532 
533 /**
534   Function for 'ls' command.
535 
536   @param[in] ImageHandle  Handle to the Image (NULL if Internal).
537   @param[in] SystemTable  Pointer to the System Table (NULL if Internal).
538 **/
539 SHELL_STATUS
540 EFIAPI
ShellCommandRunLs(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)541 ShellCommandRunLs (
542   IN EFI_HANDLE        ImageHandle,
543   IN EFI_SYSTEM_TABLE  *SystemTable
544   )
545 {
546   EFI_STATUS    Status;
547   LIST_ENTRY    *Package;
548   CHAR16        *ProblemParam;
549   CONST CHAR16  *Attribs;
550   SHELL_STATUS  ShellStatus;
551   UINT64        RequiredAttributes;
552   CONST CHAR16  *PathName;
553   CONST CHAR16  *CurDir;
554   UINTN         Count;
555   CHAR16        *FullPath;
556   UINTN         Size;
557   EFI_TIME      TheTime;
558   CHAR16        *SearchString;
559 
560   Size                = 0;
561   FullPath            = NULL;
562   ProblemParam        = NULL;
563   Attribs             = NULL;
564   ShellStatus         = SHELL_SUCCESS;
565   RequiredAttributes  = 0;
566   PathName            = NULL;
567   SearchString        = NULL;
568   CurDir              = NULL;
569   Count               = 0;
570 
571   //
572   // initialize the shell lib (we must be in non-auto-init...)
573   //
574   Status = ShellInitialize();
575   ASSERT_EFI_ERROR(Status);
576 
577   //
578   // Fix local copies of the protocol pointers
579   //
580   Status = CommandInit();
581   ASSERT_EFI_ERROR(Status);
582 
583   //
584   // parse the command line
585   //
586   Status = ShellCommandLineParse (LsParamList, &Package, &ProblemParam, TRUE);
587   if (EFI_ERROR(Status)) {
588     if (Status == EFI_VOLUME_CORRUPTED && ProblemParam != NULL) {
589       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), gShellLevel2HiiHandle, L"ls", ProblemParam);
590       FreePool(ProblemParam);
591       ShellStatus = SHELL_INVALID_PARAMETER;
592     } else {
593       ASSERT(FALSE);
594     }
595   } else {
596     //
597     // check for "-?"
598     //
599     if (ShellCommandLineGetFlag(Package, L"-?")) {
600       ASSERT(FALSE);
601     }
602 
603     if (ShellCommandLineGetCount(Package) > 2) {
604       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_MANY), gShellLevel2HiiHandle, L"ls");
605       ShellStatus = SHELL_INVALID_PARAMETER;
606     } else {
607       //
608       // check for -a
609       //
610       if (ShellCommandLineGetFlag(Package, L"-a")) {
611         for ( Attribs = ShellCommandLineGetValue(Package, L"-a")
612             ; Attribs != NULL && *Attribs != CHAR_NULL && ShellStatus == SHELL_SUCCESS
613             ; Attribs++
614            ){
615           switch (*Attribs) {
616             case L'a':
617             case L'A':
618               RequiredAttributes |= EFI_FILE_ARCHIVE;
619               Count++;
620               continue;
621             case L's':
622             case L'S':
623               RequiredAttributes |= EFI_FILE_SYSTEM;
624               Count++;
625               continue;
626             case L'h':
627             case L'H':
628               RequiredAttributes |= EFI_FILE_HIDDEN;
629               Count++;
630               continue;
631             case L'r':
632             case L'R':
633               RequiredAttributes |= EFI_FILE_READ_ONLY;
634               Count++;
635               continue;
636             case L'd':
637             case L'D':
638               RequiredAttributes |= EFI_FILE_DIRECTORY;
639               Count++;
640               continue;
641             default:
642               ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_ATTRIBUTE), gShellLevel2HiiHandle, L"ls", ShellCommandLineGetValue(Package, L"-a"));
643               ShellStatus = SHELL_INVALID_PARAMETER;
644               break;
645           } // switch
646         } // for loop
647         //
648         // if nothing is specified all are specified
649         //
650         if (RequiredAttributes == 0) {
651           RequiredAttributes = EFI_FILE_VALID_ATTR;
652         }
653       } // if -a present
654       if (ShellStatus == SHELL_SUCCESS) {
655         PathName = ShellCommandLineGetRawValue(Package, 1);
656         if (PathName == NULL) {
657           //
658           // Nothing specified... must start from current directory
659           //
660           CurDir = gEfiShellProtocol->GetCurDir(NULL);
661           if (CurDir == NULL) {
662             ShellStatus = SHELL_NOT_FOUND;
663             ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_NO_CWD), gShellLevel2HiiHandle, L"ls");
664           }
665           //
666           // Copy to the 2 strings for starting path and file search string
667           //
668           ASSERT(SearchString == NULL);
669           ASSERT(FullPath == NULL);
670           StrnCatGrow(&SearchString, NULL, L"*", 0);
671           StrnCatGrow(&FullPath, NULL, CurDir, 0);
672           Size = FullPath != NULL? StrSize(FullPath) : 0;
673           StrnCatGrow(&FullPath, &Size, L"\\", 0);
674         } else {
675           if (StrStr(PathName, L":") == NULL && gEfiShellProtocol->GetCurDir(NULL) == NULL) {
676             //
677             // If we got something and it doesnt have a fully qualified path, then we needed to have a CWD.
678             //
679             ShellStatus = SHELL_NOT_FOUND;
680             ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_NO_CWD), gShellLevel2HiiHandle, L"ls");
681           } else {
682             //
683             // We got a valid fully qualified path or we have a CWD
684             //
685             ASSERT((FullPath == NULL && Size == 0) || (FullPath != NULL));
686             if (StrStr(PathName, L":") == NULL) {
687               StrnCatGrow(&FullPath, &Size, gEfiShellProtocol->GetCurDir(NULL), 0);
688               if (FullPath == NULL) {
689                 ShellCommandLineFreeVarList (Package);
690                 return SHELL_OUT_OF_RESOURCES;
691               }
692               Size = FullPath != NULL? StrSize(FullPath) : 0;
693               StrnCatGrow(&FullPath, &Size, L"\\", 0);
694             }
695             StrnCatGrow(&FullPath, &Size, PathName, 0);
696             if (FullPath == NULL) {
697                 ShellCommandLineFreeVarList (Package);
698                 return SHELL_OUT_OF_RESOURCES;
699             }
700 
701             if  (ShellIsDirectory(PathName) == EFI_SUCCESS) {
702               //
703               // is listing ends with a directory, then we list all files in that directory
704               //
705               StrnCatGrow(&SearchString, NULL, L"*", 0);
706             } else {
707               //
708               // must split off the search part that applies to files from the end of the directory part
709               //
710               for (StrnCatGrow(&SearchString, NULL, PathName, 0)
711                 ; SearchString != NULL && StrStr(SearchString, L"\\") != NULL
712                 ; CopyMem(SearchString, StrStr(SearchString, L"\\") + 1, 1 + StrSize(StrStr(SearchString, L"\\") + 1))) ;
713               FullPath[StrLen(FullPath) - StrLen(SearchString)] = CHAR_NULL;
714             }
715           }
716         }
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;
721         }
722 
723         if (ShellStatus == SHELL_SUCCESS) {
724           ShellStatus = PrintLsOutput(
725             ShellCommandLineGetFlag(Package, L"-r"),
726             RequiredAttributes,
727             ShellCommandLineGetFlag(Package, L"-sfo"),
728             FullPath,
729             SearchString,
730             NULL,
731             Count,
732             TheTime.TimeZone
733            );
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) {
739             //
740             // Ignore aborting.
741             //
742           } else if (ShellStatus != SHELL_SUCCESS) {
743             ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellLevel2HiiHandle, L"ls", FullPath);
744           }
745         }
746       }
747     }
748   }
749 
750   //
751   // Free memory allocated
752   //
753   SHELL_FREE_NON_NULL(SearchString);
754   SHELL_FREE_NON_NULL(FullPath);
755   ShellCommandLineFreeVarList (Package);
756 
757   return (ShellStatus);
758 }
759