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