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