]> git.proxmox.com Git - mirror_edk2.git/blob - ShellPkg/Library/UefiShellLevel2CommandsLib/Ls.c
ShellPkg: Apply uncrustify changes
[mirror_edk2.git] / ShellPkg / Library / UefiShellLevel2CommandsLib / Ls.c
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 - 2018, Intel Corporation. All rights reserved.<BR>
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7
8 **/
9
10 #include "UefiShellLevel2CommandsLib.h"
11 #include <Guid/FileSystemInfo.h>
12
13 UINTN mDayOfMonth[] = { 31, 28, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30 };
14
15 /**
16 print out the standard format output volume entry.
17
18 @param[in] TheList a list of files from the volume.
19 **/
20 EFI_STATUS
21 PrintSfoVolumeInfoTableEntry (
22 IN CONST EFI_SHELL_FILE_INFO *TheList
23 )
24 {
25 EFI_STATUS Status;
26 EFI_SHELL_FILE_INFO *Node;
27 CHAR16 *DirectoryName;
28 EFI_FILE_SYSTEM_INFO *SysInfo;
29 UINTN SysInfoSize;
30 SHELL_FILE_HANDLE ShellFileHandle;
31 EFI_FILE_PROTOCOL *EfiFpHandle;
32
33 //
34 // Get the first valid handle (directories)
35 //
36 for ( Node = (EFI_SHELL_FILE_INFO *)GetFirstNode (&TheList->Link)
37 ; !IsNull (&TheList->Link, &Node->Link) && Node->Handle == NULL
38 ; Node = (EFI_SHELL_FILE_INFO *)GetNextNode (&TheList->Link, &Node->Link)
39 )
40 {
41 }
42
43 if (Node->Handle == NULL) {
44 DirectoryName = GetFullyQualifiedPath (((EFI_SHELL_FILE_INFO *)GetFirstNode (&TheList->Link))->FullName);
45
46 //
47 // We need to open something up to get system information
48 //
49 Status = gEfiShellProtocol->OpenFileByName (
50 DirectoryName,
51 &ShellFileHandle,
52 EFI_FILE_MODE_READ
53 );
54
55 ASSERT_EFI_ERROR (Status);
56 FreePool (DirectoryName);
57
58 //
59 // Get the Volume Info from ShellFileHandle
60 //
61 SysInfo = NULL;
62 SysInfoSize = 0;
63 EfiFpHandle = ConvertShellHandleToEfiFileProtocol (ShellFileHandle);
64 Status = EfiFpHandle->GetInfo (
65 EfiFpHandle,
66 &gEfiFileSystemInfoGuid,
67 &SysInfoSize,
68 SysInfo
69 );
70
71 if (Status == EFI_BUFFER_TOO_SMALL) {
72 SysInfo = AllocateZeroPool (SysInfoSize);
73 Status = EfiFpHandle->GetInfo (
74 EfiFpHandle,
75 &gEfiFileSystemInfoGuid,
76 &SysInfoSize,
77 SysInfo
78 );
79 }
80
81 ASSERT_EFI_ERROR (Status);
82
83 gEfiShellProtocol->CloseFile (ShellFileHandle);
84 } else {
85 //
86 // Get the Volume Info from Node->Handle
87 //
88 SysInfo = NULL;
89 SysInfoSize = 0;
90 EfiFpHandle = ConvertShellHandleToEfiFileProtocol (Node->Handle);
91 Status = EfiFpHandle->GetInfo (
92 EfiFpHandle,
93 &gEfiFileSystemInfoGuid,
94 &SysInfoSize,
95 SysInfo
96 );
97
98 if (Status == EFI_BUFFER_TOO_SMALL) {
99 SysInfo = AllocateZeroPool (SysInfoSize);
100 Status = EfiFpHandle->GetInfo (
101 EfiFpHandle,
102 &gEfiFileSystemInfoGuid,
103 &SysInfoSize,
104 SysInfo
105 );
106 }
107
108 ASSERT_EFI_ERROR (Status);
109 }
110
111 ShellPrintHiiEx (
112 -1,
113 -1,
114 NULL,
115 STRING_TOKEN (STR_GEN_SFO_HEADER),
116 gShellLevel2HiiHandle,
117 L"ls"
118 );
119 //
120 // print VolumeInfo table
121 //
122 ASSERT (SysInfo != NULL);
123 ShellPrintHiiEx (
124 0,
125 gST->ConOut->Mode->CursorRow,
126 NULL,
127 STRING_TOKEN (STR_LS_SFO_VOLINFO),
128 gShellLevel2HiiHandle,
129 SysInfo->VolumeLabel,
130 SysInfo->VolumeSize,
131 SysInfo->ReadOnly ? L"TRUE" : L"FALSE",
132 SysInfo->FreeSpace,
133 SysInfo->BlockSize
134 );
135
136 SHELL_FREE_NON_NULL (SysInfo);
137
138 return (Status);
139 }
140
141 /**
142 print out the info on a single file.
143
144 @param[in] Sfo TRUE if in SFO, false otherwise.
145 @param[in] TheNode the EFI_SHELL_FILE_INFO node to print out information on.
146 @param[in] Files incremented if a file is printed.
147 @param[in] Size incremented by file size.
148 @param[in] Dirs incremented if a directory is printed.
149
150 **/
151 VOID
152 PrintFileInformation (
153 IN CONST BOOLEAN Sfo,
154 IN CONST EFI_SHELL_FILE_INFO *TheNode,
155 IN UINT64 *Files,
156 IN UINT64 *Size,
157 IN UINT64 *Dirs
158 )
159 {
160 ASSERT (Files != NULL);
161 ASSERT (Size != NULL);
162 ASSERT (Dirs != NULL);
163 ASSERT (TheNode != NULL);
164
165 if (Sfo) {
166 //
167 // Print the FileInfo Table
168 //
169 ShellPrintHiiEx (
170 0,
171 gST->ConOut->Mode->CursorRow,
172 NULL,
173 STRING_TOKEN (STR_LS_SFO_FILEINFO),
174 gShellLevel2HiiHandle,
175 TheNode->FullName,
176 TheNode->Info->FileSize,
177 TheNode->Info->PhysicalSize,
178 (TheNode->Info->Attribute & EFI_FILE_ARCHIVE) != 0 ? L"a" : L"",
179 (TheNode->Info->Attribute & EFI_FILE_DIRECTORY) != 0 ? L"d" : L"",
180 (TheNode->Info->Attribute & EFI_FILE_HIDDEN) != 0 ? L"h" : L"",
181 (TheNode->Info->Attribute & EFI_FILE_READ_ONLY) != 0 ? L"r" : L"",
182 (TheNode->Info->Attribute & EFI_FILE_SYSTEM) != 0 ? L"s" : L"",
183 TheNode->Info->CreateTime.Hour,
184 TheNode->Info->CreateTime.Minute,
185 TheNode->Info->CreateTime.Second,
186 TheNode->Info->CreateTime.Day,
187 TheNode->Info->CreateTime.Month,
188 TheNode->Info->CreateTime.Year,
189 TheNode->Info->LastAccessTime.Hour,
190 TheNode->Info->LastAccessTime.Minute,
191 TheNode->Info->LastAccessTime.Second,
192 TheNode->Info->LastAccessTime.Day,
193 TheNode->Info->LastAccessTime.Month,
194 TheNode->Info->LastAccessTime.Year,
195 TheNode->Info->ModificationTime.Hour,
196 TheNode->Info->ModificationTime.Minute,
197 TheNode->Info->ModificationTime.Second,
198 TheNode->Info->ModificationTime.Day,
199 TheNode->Info->ModificationTime.Month,
200 TheNode->Info->ModificationTime.Year
201 );
202 } else {
203 //
204 // print this one out...
205 // first print the universal start, next print the type specific name format, last print the CRLF
206 //
207 ShellPrintHiiEx (
208 -1,
209 -1,
210 NULL,
211 STRING_TOKEN (STR_LS_LINE_START_ALL),
212 gShellLevel2HiiHandle,
213 &TheNode->Info->ModificationTime,
214 (TheNode->Info->Attribute & EFI_FILE_DIRECTORY) != 0 ? L"<DIR>" : L"",
215 (TheNode->Info->Attribute & EFI_FILE_READ_ONLY) != 0 ? L'r' : L' ',
216 TheNode->Info->FileSize
217 );
218 if (TheNode->Info->Attribute & EFI_FILE_DIRECTORY) {
219 (*Dirs)++;
220 ShellPrintHiiEx (
221 -1,
222 -1,
223 NULL,
224 STRING_TOKEN (STR_LS_LINE_END_DIR),
225 gShellLevel2HiiHandle,
226 TheNode->FileName
227 );
228 } else {
229 (*Files)++;
230 (*Size) += TheNode->Info->FileSize;
231 if ( (gUnicodeCollation->StriColl (gUnicodeCollation, (CHAR16 *)L".nsh", (CHAR16 *)&(TheNode->FileName[StrLen (TheNode->FileName) - 4])) == 0)
232 || (gUnicodeCollation->StriColl (gUnicodeCollation, (CHAR16 *)L".efi", (CHAR16 *)&(TheNode->FileName[StrLen (TheNode->FileName) - 4])) == 0)
233 )
234 {
235 ShellPrintHiiEx (
236 -1,
237 -1,
238 NULL,
239 STRING_TOKEN (STR_LS_LINE_END_EXE),
240 gShellLevel2HiiHandle,
241 TheNode->FileName
242 );
243 } else {
244 ShellPrintHiiEx (
245 -1,
246 -1,
247 NULL,
248 STRING_TOKEN (STR_LS_LINE_END_FILE),
249 gShellLevel2HiiHandle,
250 TheNode->FileName
251 );
252 }
253 }
254 }
255 }
256
257 /**
258 print out the header when not using standard format output.
259
260 @param[in] Path String with starting path.
261 **/
262 VOID
263 PrintNonSfoHeader (
264 IN CONST CHAR16 *Path
265 )
266 {
267 CHAR16 *DirectoryName;
268
269 //
270 // get directory name from path...
271 //
272 DirectoryName = GetFullyQualifiedPath (Path);
273
274 if (DirectoryName != NULL) {
275 //
276 // print header
277 //
278 ShellPrintHiiEx (
279 0,
280 gST->ConOut->Mode->CursorRow,
281 NULL,
282 STRING_TOKEN (STR_LS_HEADER_LINE1),
283 gShellLevel2HiiHandle,
284 DirectoryName
285 );
286
287 SHELL_FREE_NON_NULL (DirectoryName);
288 }
289 }
290
291 /**
292 print out the footer when not using standard format output.
293
294 @param[in] Files The number of files.
295 @param[in] Size The size of files in bytes.
296 @param[in] Dirs The number of directories.
297 **/
298 VOID
299 PrintNonSfoFooter (
300 IN UINT64 Files,
301 IN UINT64 Size,
302 IN UINT64 Dirs
303 )
304 {
305 //
306 // print footer
307 //
308 ShellPrintHiiEx (
309 -1,
310 -1,
311 NULL,
312 STRING_TOKEN (STR_LS_FOOTER_LINE),
313 gShellLevel2HiiHandle,
314 Files,
315 Size,
316 Dirs
317 );
318 }
319
320 /**
321 Change the file time to local time based on the timezone.
322
323 @param[in] Time The file time.
324 @param[in] LocalTimeZone Local time zone.
325 **/
326 VOID
327 FileTimeToLocalTime (
328 IN EFI_TIME *Time,
329 IN INT16 LocalTimeZone
330 )
331 {
332 INTN MinuteDiff;
333 INTN TempMinute;
334 INTN HourNumberOfTempMinute;
335 INTN TempHour;
336 INTN DayNumberOfTempHour;
337 INTN TempDay;
338 INTN MonthNumberOfTempDay;
339 INTN TempMonth;
340 INTN YearNumberOfTempMonth;
341 INTN MonthRecord;
342
343 ASSERT ((Time->TimeZone >= -1440) && (Time->TimeZone <= 1440));
344 ASSERT ((LocalTimeZone >= -1440) && (LocalTimeZone <= 1440));
345 ASSERT ((Time->Month >= 1) && (Time->Month <= 12));
346
347 if (Time->TimeZone == LocalTimeZone) {
348 //
349 // if the file timezone is equal to the local timezone, there is no need to adjust the file time.
350 //
351 return;
352 }
353
354 if (((Time->Year % 4 == 0) && (Time->Year / 100 != 0)) || (Time->Year % 400 == 0)) {
355 //
356 // Day in February of leap year is 29.
357 //
358 mDayOfMonth[1] = 29;
359 }
360
361 MinuteDiff = Time->TimeZone - LocalTimeZone;
362 TempMinute = Time->Minute + MinuteDiff;
363
364 //
365 // Calculate Time->Minute
366 // TempHour will be used to calculate Time->Hour
367 //
368 HourNumberOfTempMinute = TempMinute / 60;
369 if (TempMinute < 0) {
370 HourNumberOfTempMinute--;
371 }
372
373 TempHour = Time->Hour + HourNumberOfTempMinute;
374 Time->Minute = (UINT8)(TempMinute - 60 * HourNumberOfTempMinute);
375
376 //
377 // Calculate Time->Hour
378 // TempDay will be used to calculate Time->Day
379 //
380 DayNumberOfTempHour = TempHour / 24;
381 if (TempHour < 0) {
382 DayNumberOfTempHour--;
383 }
384
385 TempDay = Time->Day + DayNumberOfTempHour;
386 Time->Hour = (UINT8)(TempHour - 24 * DayNumberOfTempHour);
387
388 //
389 // Calculate Time->Day
390 // TempMonth will be used to calculate Time->Month
391 //
392 MonthNumberOfTempDay = (TempDay - 1) / (INTN)mDayOfMonth[Time->Month - 1];
393 MonthRecord = (INTN)(Time->Month);
394 if (TempDay - 1 < 0) {
395 MonthNumberOfTempDay--;
396 MonthRecord--;
397 }
398
399 TempMonth = Time->Month + MonthNumberOfTempDay;
400 Time->Day = (UINT8)(TempDay - (INTN)mDayOfMonth[(MonthRecord - 1 + 12) % 12] * MonthNumberOfTempDay);
401
402 //
403 // Calculate Time->Month, Time->Year
404 //
405 YearNumberOfTempMonth = (TempMonth - 1) / 12;
406 if (TempMonth - 1 < 0) {
407 YearNumberOfTempMonth--;
408 }
409
410 Time->Month = (UINT8)(TempMonth - 12 * (YearNumberOfTempMonth));
411 Time->Year = (UINT16)(Time->Year + YearNumberOfTempMonth);
412 }
413
414 /**
415 print out the list of files and directories from the LS command
416
417 @param[in] Rec TRUE to automatically recurse into each found directory
418 FALSE to only list the specified directory.
419 @param[in] Attribs List of required Attribute for display.
420 If 0 then all non-system and non-hidden files will be printed.
421 @param[in] Sfo TRUE to use Standard Format Output, FALSE otherwise
422 @param[in] RootPath String with starting path to search in.
423 @param[in] SearchString String with search string.
424 @param[in] Found Set to TRUE, if anyone were found.
425 @param[in] Count The count of bits enabled in Attribs.
426 @param[in] TimeZone The current time zone offset.
427 @param[in] ListUnfiltered TRUE to request listing the directory contents
428 unfiltered.
429
430 @retval SHELL_SUCCESS the printing was sucessful.
431 **/
432 SHELL_STATUS
433 PrintLsOutput (
434 IN CONST BOOLEAN Rec,
435 IN CONST UINT64 Attribs,
436 IN CONST BOOLEAN Sfo,
437 IN CONST CHAR16 *RootPath,
438 IN CONST CHAR16 *SearchString,
439 IN BOOLEAN *Found,
440 IN CONST UINTN Count,
441 IN CONST INT16 TimeZone,
442 IN CONST BOOLEAN ListUnfiltered
443 )
444 {
445 EFI_STATUS Status;
446 EFI_SHELL_FILE_INFO *ListHead;
447 EFI_SHELL_FILE_INFO *Node;
448 SHELL_STATUS ShellStatus;
449 UINT64 FileCount;
450 UINT64 DirCount;
451 UINT64 FileSize;
452 UINTN LongestPath;
453 CHAR16 *CorrectedPath;
454 BOOLEAN FoundOne;
455 BOOLEAN HeaderPrinted;
456 EFI_TIME LocalTime;
457
458 HeaderPrinted = FALSE;
459 FileCount = 0;
460 DirCount = 0;
461 FileSize = 0;
462 ListHead = NULL;
463 ShellStatus = SHELL_SUCCESS;
464 LongestPath = 0;
465 CorrectedPath = NULL;
466
467 if (Found != NULL) {
468 FoundOne = *Found;
469 } else {
470 FoundOne = FALSE;
471 }
472
473 CorrectedPath = StrnCatGrow (&CorrectedPath, &LongestPath, RootPath, 0);
474 if (CorrectedPath == NULL) {
475 return SHELL_OUT_OF_RESOURCES;
476 }
477
478 if ( (CorrectedPath[StrLen (CorrectedPath)-1] != L'\\')
479 && (CorrectedPath[StrLen (CorrectedPath)-1] != L'/'))
480 {
481 CorrectedPath = StrnCatGrow (&CorrectedPath, &LongestPath, L"\\", 0);
482 }
483
484 CorrectedPath = StrnCatGrow (&CorrectedPath, &LongestPath, SearchString, 0);
485 if (CorrectedPath == NULL) {
486 return (SHELL_OUT_OF_RESOURCES);
487 }
488
489 PathCleanUpDirectories (CorrectedPath);
490
491 Status = ShellOpenFileMetaArg ((CHAR16 *)CorrectedPath, EFI_FILE_MODE_READ, &ListHead);
492 if (!EFI_ERROR (Status)) {
493 if ((ListHead == NULL) || IsListEmpty (&ListHead->Link)) {
494 SHELL_FREE_NON_NULL (CorrectedPath);
495 return (SHELL_SUCCESS);
496 }
497
498 if (Sfo && (Found == NULL)) {
499 PrintSfoVolumeInfoTableEntry (ListHead);
500 }
501
502 if (!Sfo) {
503 //
504 // Sort the file list by FileName, stably.
505 //
506 // If the call below fails, then the EFI_SHELL_FILE_INFO list anchored to
507 // ListHead will not be changed in any way.
508 //
509 ShellSortFileList (
510 &ListHead,
511 NULL, // Duplicates
512 ShellSortFileListByFileName
513 );
514 }
515
516 for ( Node = (EFI_SHELL_FILE_INFO *)GetFirstNode (&ListHead->Link), LongestPath = 0
517 ; !IsNull (&ListHead->Link, &Node->Link)
518 ; Node = (EFI_SHELL_FILE_INFO *)GetNextNode (&ListHead->Link, &Node->Link)
519 )
520 {
521 if (ShellGetExecutionBreakFlag ()) {
522 ShellStatus = SHELL_ABORTED;
523 break;
524 }
525
526 ASSERT (Node != NULL);
527
528 //
529 // Change the file time to local time.
530 //
531 Status = gRT->GetTime (&LocalTime, NULL);
532 if (!EFI_ERROR (Status) && (LocalTime.TimeZone != EFI_UNSPECIFIED_TIMEZONE)) {
533 if ((Node->Info->CreateTime.TimeZone != EFI_UNSPECIFIED_TIMEZONE) &&
534 ((Node->Info->CreateTime.Month >= 1) && (Node->Info->CreateTime.Month <= 12)))
535 {
536 //
537 // FileTimeToLocalTime () requires Month is in a valid range, other buffer out-of-band access happens.
538 //
539 FileTimeToLocalTime (&Node->Info->CreateTime, LocalTime.TimeZone);
540 }
541
542 if ((Node->Info->LastAccessTime.TimeZone != EFI_UNSPECIFIED_TIMEZONE) &&
543 ((Node->Info->LastAccessTime.Month >= 1) && (Node->Info->LastAccessTime.Month <= 12)))
544 {
545 FileTimeToLocalTime (&Node->Info->LastAccessTime, LocalTime.TimeZone);
546 }
547
548 if ((Node->Info->ModificationTime.TimeZone != EFI_UNSPECIFIED_TIMEZONE) &&
549 ((Node->Info->ModificationTime.Month >= 1) && (Node->Info->ModificationTime.Month <= 12)))
550 {
551 FileTimeToLocalTime (&Node->Info->ModificationTime, LocalTime.TimeZone);
552 }
553 }
554
555 if (LongestPath < StrSize (Node->FullName)) {
556 LongestPath = StrSize (Node->FullName);
557 }
558
559 ASSERT (Node->Info != NULL);
560 ASSERT ((Node->Info->Attribute & EFI_FILE_VALID_ATTR) == Node->Info->Attribute);
561 if (Attribs == 0) {
562 //
563 // NOT system & NOT hidden
564 //
565 if ( (Node->Info->Attribute & EFI_FILE_SYSTEM)
566 || (Node->Info->Attribute & EFI_FILE_HIDDEN)
567 )
568 {
569 continue;
570 }
571 } else if ((Attribs != EFI_FILE_VALID_ATTR) ||
572 (Count == 5))
573 {
574 //
575 // Only matches the bits which "Attribs" contains, not
576 // all files/directories with any of the bits.
577 // Count == 5 is used to tell the difference between a user
578 // specifying all bits (EX: -arhsda) and just specifying
579 // -a (means display all files with any attribute).
580 //
581 if ((Node->Info->Attribute & Attribs) != Attribs) {
582 continue;
583 }
584 }
585
586 if (!Sfo && !HeaderPrinted) {
587 PathRemoveLastItem (CorrectedPath);
588 PrintNonSfoHeader (CorrectedPath);
589 }
590
591 PrintFileInformation (Sfo, Node, &FileCount, &FileSize, &DirCount);
592 FoundOne = TRUE;
593 HeaderPrinted = TRUE;
594 }
595
596 if (!Sfo && (ShellStatus != SHELL_ABORTED) && HeaderPrinted) {
597 PrintNonSfoFooter (FileCount, FileSize, DirCount);
598 }
599 }
600
601 if (Rec && (ShellStatus != SHELL_ABORTED)) {
602 //
603 // Re-Open all the files under the starting path for directories that didnt necessarily match our file filter
604 //
605 ShellCloseFileMetaArg (&ListHead);
606 CorrectedPath[0] = CHAR_NULL;
607 CorrectedPath = StrnCatGrow (&CorrectedPath, &LongestPath, RootPath, 0);
608 if (CorrectedPath == NULL) {
609 return SHELL_OUT_OF_RESOURCES;
610 }
611
612 if ( (CorrectedPath[StrLen (CorrectedPath)-1] != L'\\')
613 && (CorrectedPath[StrLen (CorrectedPath)-1] != L'/'))
614 {
615 CorrectedPath = StrnCatGrow (&CorrectedPath, &LongestPath, L"\\", 0);
616 }
617
618 CorrectedPath = StrnCatGrow (&CorrectedPath, &LongestPath, L"*", 0);
619 Status = ShellOpenFileMetaArg ((CHAR16 *)CorrectedPath, EFI_FILE_MODE_READ, &ListHead);
620
621 if (!EFI_ERROR (Status)) {
622 for ( Node = (EFI_SHELL_FILE_INFO *)GetFirstNode (&ListHead->Link)
623 ; !IsNull (&ListHead->Link, &Node->Link) && ShellStatus == SHELL_SUCCESS
624 ; Node = (EFI_SHELL_FILE_INFO *)GetNextNode (&ListHead->Link, &Node->Link)
625 )
626 {
627 if (ShellGetExecutionBreakFlag ()) {
628 ShellStatus = SHELL_ABORTED;
629 break;
630 }
631
632 //
633 // recurse on any directory except the traversing ones...
634 //
635 if ( ((Node->Info->Attribute & EFI_FILE_DIRECTORY) == EFI_FILE_DIRECTORY)
636 && (StrCmp (Node->FileName, L".") != 0)
637 && (StrCmp (Node->FileName, L"..") != 0)
638 )
639 {
640 ShellStatus = PrintLsOutput (
641 Rec,
642 Attribs,
643 Sfo,
644 Node->FullName,
645 SearchString,
646 &FoundOne,
647 Count,
648 TimeZone,
649 FALSE
650 );
651
652 //
653 // Since it's running recursively, we have to break immediately when returned SHELL_ABORTED
654 //
655 if (ShellStatus == SHELL_ABORTED) {
656 break;
657 }
658 }
659 }
660 }
661 }
662
663 SHELL_FREE_NON_NULL (CorrectedPath);
664 ShellCloseFileMetaArg (&ListHead);
665
666 if ((Found == NULL) && !FoundOne) {
667 if (ListUnfiltered) {
668 //
669 // When running "ls" without any filtering request, avoid outputing
670 // "File not found" when the directory is entirely empty, but print
671 // header and footer stating "0 File(s), 0 Dir(s)".
672 //
673 if (!Sfo) {
674 PrintNonSfoHeader (RootPath);
675 if (ShellStatus != SHELL_ABORTED) {
676 PrintNonSfoFooter (FileCount, FileSize, DirCount);
677 }
678 }
679 } else {
680 return (SHELL_NOT_FOUND);
681 }
682 }
683
684 if (Found != NULL) {
685 *Found = FoundOne;
686 }
687
688 return (ShellStatus);
689 }
690
691 STATIC CONST SHELL_PARAM_ITEM LsParamList[] = {
692 { L"-r", TypeFlag },
693 { L"-a", TypeStart },
694 { L"-sfo", TypeFlag },
695 { NULL, TypeMax }
696 };
697
698 /**
699 Function for 'ls' command.
700
701 @param[in] ImageHandle Handle to the Image (NULL if Internal).
702 @param[in] SystemTable Pointer to the System Table (NULL if Internal).
703 **/
704 SHELL_STATUS
705 EFIAPI
706 ShellCommandRunLs (
707 IN EFI_HANDLE ImageHandle,
708 IN EFI_SYSTEM_TABLE *SystemTable
709 )
710 {
711 EFI_STATUS Status;
712 LIST_ENTRY *Package;
713 CHAR16 *ProblemParam;
714 CONST CHAR16 *Attribs;
715 SHELL_STATUS ShellStatus;
716 UINT64 RequiredAttributes;
717 CONST CHAR16 *PathName;
718 CONST CHAR16 *CurDir;
719 UINTN Count;
720 CHAR16 *FullPath;
721 UINTN Size;
722 EFI_TIME TheTime;
723 CHAR16 *SearchString;
724 BOOLEAN ListUnfiltered;
725
726 Size = 0;
727 FullPath = NULL;
728 ProblemParam = NULL;
729 Attribs = NULL;
730 ShellStatus = SHELL_SUCCESS;
731 RequiredAttributes = 0;
732 PathName = NULL;
733 SearchString = NULL;
734 CurDir = NULL;
735 Count = 0;
736 ListUnfiltered = FALSE;
737
738 //
739 // initialize the shell lib (we must be in non-auto-init...)
740 //
741 Status = ShellInitialize ();
742 ASSERT_EFI_ERROR (Status);
743
744 //
745 // Fix local copies of the protocol pointers
746 //
747 Status = CommandInit ();
748 ASSERT_EFI_ERROR (Status);
749
750 //
751 // parse the command line
752 //
753 Status = ShellCommandLineParse (LsParamList, &Package, &ProblemParam, TRUE);
754 if (EFI_ERROR (Status)) {
755 if ((Status == EFI_VOLUME_CORRUPTED) && (ProblemParam != NULL)) {
756 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), gShellLevel2HiiHandle, L"ls", ProblemParam);
757 FreePool (ProblemParam);
758 ShellStatus = SHELL_INVALID_PARAMETER;
759 } else {
760 ASSERT (FALSE);
761 }
762 } else {
763 //
764 // check for "-?"
765 //
766 if (ShellCommandLineGetFlag (Package, L"-?")) {
767 ASSERT (FALSE);
768 }
769
770 if (ShellCommandLineGetCount (Package) > 2) {
771 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_MANY), gShellLevel2HiiHandle, L"ls");
772 ShellStatus = SHELL_INVALID_PARAMETER;
773 } else {
774 //
775 // check for -a
776 //
777 if (ShellCommandLineGetFlag (Package, L"-a")) {
778 for ( Attribs = ShellCommandLineGetValue (Package, L"-a")
779 ; Attribs != NULL && *Attribs != CHAR_NULL && ShellStatus == SHELL_SUCCESS
780 ; Attribs++
781 )
782 {
783 switch (*Attribs) {
784 case L'a':
785 case L'A':
786 RequiredAttributes |= EFI_FILE_ARCHIVE;
787 Count++;
788 continue;
789 case L's':
790 case L'S':
791 RequiredAttributes |= EFI_FILE_SYSTEM;
792 Count++;
793 continue;
794 case L'h':
795 case L'H':
796 RequiredAttributes |= EFI_FILE_HIDDEN;
797 Count++;
798 continue;
799 case L'r':
800 case L'R':
801 RequiredAttributes |= EFI_FILE_READ_ONLY;
802 Count++;
803 continue;
804 case L'd':
805 case L'D':
806 RequiredAttributes |= EFI_FILE_DIRECTORY;
807 Count++;
808 continue;
809 default:
810 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_ATTRIBUTE), gShellLevel2HiiHandle, L"ls", ShellCommandLineGetValue (Package, L"-a"));
811 ShellStatus = SHELL_INVALID_PARAMETER;
812 break;
813 } // switch
814 } // for loop
815
816 //
817 // if nothing is specified all are specified
818 //
819 if (RequiredAttributes == 0) {
820 RequiredAttributes = EFI_FILE_VALID_ATTR;
821 }
822 } // if -a present
823
824 if (ShellStatus == SHELL_SUCCESS) {
825 PathName = ShellCommandLineGetRawValue (Package, 1);
826 if (PathName == NULL) {
827 //
828 // Nothing specified... must start from current directory
829 //
830 CurDir = gEfiShellProtocol->GetCurDir (NULL);
831 if (CurDir == NULL) {
832 ShellStatus = SHELL_NOT_FOUND;
833 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_NO_CWD), gShellLevel2HiiHandle, L"ls");
834 }
835
836 ListUnfiltered = TRUE;
837 //
838 // Copy to the 2 strings for starting path and file search string
839 //
840 ASSERT (SearchString == NULL);
841 ASSERT (FullPath == NULL);
842 StrnCatGrow (&SearchString, NULL, L"*", 0);
843 StrnCatGrow (&FullPath, NULL, CurDir, 0);
844 Size = FullPath != NULL ? StrSize (FullPath) : 0;
845 StrnCatGrow (&FullPath, &Size, L"\\", 0);
846 } else {
847 if ((StrStr (PathName, L":") == NULL) && (gEfiShellProtocol->GetCurDir (NULL) == NULL)) {
848 //
849 // If we got something and it doesnt have a fully qualified path, then we needed to have a CWD.
850 //
851 ShellStatus = SHELL_NOT_FOUND;
852 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_NO_CWD), gShellLevel2HiiHandle, L"ls");
853 } else {
854 //
855 // We got a valid fully qualified path or we have a CWD
856 //
857 ASSERT ((FullPath == NULL && Size == 0) || (FullPath != NULL));
858 if (StrStr (PathName, L":") == NULL) {
859 StrnCatGrow (&FullPath, &Size, gEfiShellProtocol->GetCurDir (NULL), 0);
860 if (FullPath == NULL) {
861 ShellCommandLineFreeVarList (Package);
862 return SHELL_OUT_OF_RESOURCES;
863 }
864
865 Size = FullPath != NULL ? StrSize (FullPath) : 0;
866 StrnCatGrow (&FullPath, &Size, L"\\", 0);
867 }
868
869 StrnCatGrow (&FullPath, &Size, PathName, 0);
870 if (FullPath == NULL) {
871 ShellCommandLineFreeVarList (Package);
872 return SHELL_OUT_OF_RESOURCES;
873 }
874
875 if (ShellIsDirectory (PathName) == EFI_SUCCESS) {
876 //
877 // is listing ends with a directory, then we list all files in that directory
878 //
879 ListUnfiltered = TRUE;
880 StrnCatGrow (&SearchString, NULL, L"*", 0);
881 } else {
882 //
883 // must split off the search part that applies to files from the end of the directory part
884 //
885 StrnCatGrow (&SearchString, NULL, FullPath, 0);
886 if (SearchString == NULL) {
887 FreePool (FullPath);
888 ShellCommandLineFreeVarList (Package);
889 return SHELL_OUT_OF_RESOURCES;
890 }
891
892 PathRemoveLastItem (FullPath);
893 CopyMem (SearchString, SearchString + StrLen (FullPath), StrSize (SearchString + StrLen (FullPath)));
894 }
895 }
896 }
897
898 Status = gRT->GetTime (&TheTime, NULL);
899 if (EFI_ERROR (Status)) {
900 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_UEFI_FUNC_WARN), gShellLevel2HiiHandle, L"ls", L"gRT->GetTime", Status);
901 TheTime.TimeZone = EFI_UNSPECIFIED_TIMEZONE;
902 }
903
904 if (ShellStatus == SHELL_SUCCESS) {
905 ShellStatus = PrintLsOutput (
906 ShellCommandLineGetFlag (Package, L"-r"),
907 RequiredAttributes,
908 ShellCommandLineGetFlag (Package, L"-sfo"),
909 FullPath,
910 SearchString,
911 NULL,
912 Count,
913 TheTime.TimeZone,
914 ListUnfiltered
915 );
916 if (ShellStatus == SHELL_NOT_FOUND) {
917 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_LS_FILE_NOT_FOUND), gShellLevel2HiiHandle, L"ls", FullPath);
918 } else if (ShellStatus == SHELL_INVALID_PARAMETER) {
919 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellLevel2HiiHandle, L"ls", FullPath);
920 } else if (ShellStatus == SHELL_ABORTED) {
921 //
922 // Ignore aborting.
923 //
924 } else if (ShellStatus != SHELL_SUCCESS) {
925 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellLevel2HiiHandle, L"ls", FullPath);
926 }
927 }
928 }
929 }
930 }
931
932 //
933 // Free memory allocated
934 //
935 SHELL_FREE_NON_NULL (SearchString);
936 SHELL_FREE_NON_NULL (FullPath);
937 ShellCommandLineFreeVarList (Package);
938
939 return (ShellStatus);
940 }