]> git.proxmox.com Git - mirror_edk2.git/blame - ShellPkg/Library/UefiShellLevel2CommandsLib/Ls.c
ShellPkg/Ls: sort output by FileName in non-SFO mode
[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 491\r
70254306
LE
492 if (!Sfo) {\r
493 //\r
494 // Sort the file list by FileName, stably.\r
495 //\r
496 // If the call below fails, then the EFI_SHELL_FILE_INFO list anchored to\r
497 // ListHead will not be changed in any way.\r
498 //\r
499 ShellSortFileList (\r
500 &ListHead,\r
501 NULL, // Duplicates\r
502 ShellSortFileListByFileName\r
503 );\r
504 }\r
505\r
929fb3be
JC
506 for ( Node = (EFI_SHELL_FILE_INFO *)GetFirstNode(&ListHead->Link), LongestPath = 0\r
507 ; !IsNull(&ListHead->Link, &Node->Link)\r
508 ; Node = (EFI_SHELL_FILE_INFO *)GetNextNode(&ListHead->Link, &Node->Link)\r
509 ){\r
8a3146d4
TS
510 if (ShellGetExecutionBreakFlag ()) {\r
511 ShellStatus = SHELL_ABORTED;\r
512 break;\r
513 }\r
929fb3be 514 ASSERT(Node != NULL);\r
99849a90
LH
515\r
516 //\r
517 // Change the file time to local time.\r
518 //\r
519 Status = gRT->GetTime(&LocalTime, NULL);\r
106369fe 520 if (!EFI_ERROR (Status) && (LocalTime.TimeZone != EFI_UNSPECIFIED_TIMEZONE)) {\r
99849a90
LH
521 if ((Node->Info->CreateTime.TimeZone != EFI_UNSPECIFIED_TIMEZONE) &&\r
522 (Node->Info->CreateTime.Month >= 1 && Node->Info->CreateTime.Month <= 12)) {\r
523 //\r
524 // FileTimeToLocalTime () requires Month is in a valid range, other buffer out-of-band access happens.\r
525 //\r
526 FileTimeToLocalTime (&Node->Info->CreateTime, LocalTime.TimeZone);\r
527 }\r
528 if ((Node->Info->LastAccessTime.TimeZone != EFI_UNSPECIFIED_TIMEZONE) &&\r
529 (Node->Info->LastAccessTime.Month >= 1 && Node->Info->LastAccessTime.Month <= 12)) {\r
530 FileTimeToLocalTime (&Node->Info->LastAccessTime, LocalTime.TimeZone);\r
531 }\r
532 if ((Node->Info->ModificationTime.TimeZone != EFI_UNSPECIFIED_TIMEZONE) &&\r
533 (Node->Info->ModificationTime.Month >= 1 && Node->Info->ModificationTime.Month <= 12)) {\r
534 FileTimeToLocalTime (&Node->Info->ModificationTime, LocalTime.TimeZone);\r
535 }\r
536 }\r
537\r
929fb3be
JC
538 if (LongestPath < StrSize(Node->FullName)) {\r
539 LongestPath = StrSize(Node->FullName);\r
a405b86d 540 }\r
929fb3be
JC
541 ASSERT(Node->Info != NULL);\r
542 ASSERT((Node->Info->Attribute & EFI_FILE_VALID_ATTR) == Node->Info->Attribute);\r
543 if (Attribs == 0) {\r
544 //\r
545 // NOT system & NOT hidden\r
546 //\r
547 if ( (Node->Info->Attribute & EFI_FILE_SYSTEM)\r
548 || (Node->Info->Attribute & EFI_FILE_HIDDEN)\r
549 ){\r
550 continue;\r
551 }\r
552 } else if ((Attribs != EFI_FILE_VALID_ATTR) ||\r
553 (Count == 5)) {\r
554 //\r
555 // Only matches the bits which "Attribs" contains, not\r
556 // all files/directories with any of the bits.\r
557 // Count == 5 is used to tell the difference between a user\r
558 // specifying all bits (EX: -arhsda) and just specifying\r
559 // -a (means display all files with any attribute).\r
560 //\r
561 if ( (Node->Info->Attribute & Attribs) != Attribs) {\r
562 continue;\r
563 }\r
a405b86d 564 }\r
a405b86d 565\r
4f344fff 566 if (!Sfo && !HeaderPrinted) {\r
5aeafb3a 567 PathRemoveLastItem (CorrectedPath);\r
929fb3be
JC
568 PrintNonSfoHeader(CorrectedPath);\r
569 }\r
570 PrintFileInformation(Sfo, Node, &FileCount, &FileSize, &DirCount);\r
571 FoundOne = TRUE;\r
572 HeaderPrinted = TRUE;\r
573 }\r
a405b86d 574\r
8d3f4281 575 if (!Sfo && ShellStatus != SHELL_ABORTED && HeaderPrinted) {\r
929fb3be
JC
576 PrintNonSfoFooter(FileCount, FileSize, DirCount);\r
577 }\r
a405b86d 578 }\r
579\r
8a3146d4 580 if (Rec && ShellStatus != SHELL_ABORTED) {\r
929fb3be
JC
581 //\r
582 // Re-Open all the files under the starting path for directories that didnt necessarily match our file filter\r
583 //\r
584 ShellCloseFileMetaArg(&ListHead);\r
585 CorrectedPath[0] = CHAR_NULL;\r
586 CorrectedPath = StrnCatGrow(&CorrectedPath, &LongestPath, RootPath, 0);\r
4f344fff
SQ
587 if (CorrectedPath == NULL) {\r
588 return SHELL_OUT_OF_RESOURCES;\r
589 }\r
929fb3be
JC
590 if (CorrectedPath[StrLen(CorrectedPath)-1] != L'\\'\r
591 &&CorrectedPath[StrLen(CorrectedPath)-1] != L'/') {\r
592 CorrectedPath = StrnCatGrow(&CorrectedPath, &LongestPath, L"\\", 0);\r
593 }\r
594 CorrectedPath = StrnCatGrow(&CorrectedPath, &LongestPath, L"*", 0);\r
595 Status = ShellOpenFileMetaArg((CHAR16*)CorrectedPath, EFI_FILE_MODE_READ, &ListHead);\r
ba0014b9 596\r
929fb3be 597 if (!EFI_ERROR(Status)) {\r
9ea69f8a 598 for ( Node = (EFI_SHELL_FILE_INFO *)GetFirstNode(&ListHead->Link)\r
a7224eef 599 ; !IsNull(&ListHead->Link, &Node->Link) && ShellStatus == SHELL_SUCCESS\r
9ea69f8a 600 ; Node = (EFI_SHELL_FILE_INFO *)GetNextNode(&ListHead->Link, &Node->Link)\r
601 ){\r
a7224eef 602 if (ShellGetExecutionBreakFlag ()) {\r
603 ShellStatus = SHELL_ABORTED;\r
604 break;\r
605 }\r
606\r
9ea69f8a 607 //\r
608 // recurse on any directory except the traversing ones...\r
609 //\r
610 if (((Node->Info->Attribute & EFI_FILE_DIRECTORY) == EFI_FILE_DIRECTORY)\r
611 && StrCmp(Node->FileName, L".") != 0\r
612 && StrCmp(Node->FileName, L"..") != 0\r
613 ){\r
a7224eef 614 ShellStatus = PrintLsOutput(\r
9ea69f8a 615 Rec,\r
616 Attribs,\r
617 Sfo,\r
929fb3be
JC
618 Node->FullName,\r
619 SearchString,\r
620 &FoundOne,\r
9ea69f8a 621 Count,\r
8d3f4281
MH
622 TimeZone,\r
623 FALSE);\r
ba0014b9 624\r
8a3146d4
TS
625 //\r
626 // Since it's running recursively, we have to break immediately when returned SHELL_ABORTED\r
627 //\r
628 if (ShellStatus == SHELL_ABORTED) {\r
629 break;\r
630 }\r
9ea69f8a 631 }\r
a405b86d 632 }\r
633 }\r
a405b86d 634 }\r
635\r
929fb3be 636 SHELL_FREE_NON_NULL(CorrectedPath);\r
a405b86d 637 ShellCloseFileMetaArg(&ListHead);\r
929fb3be 638\r
4f344fff 639 if (Found == NULL && !FoundOne) {\r
8d3f4281
MH
640 if (ListUnfiltered) {\r
641 //\r
642 // When running "ls" without any filtering request, avoid outputing\r
643 // "File not found" when the directory is entirely empty, but print\r
644 // header and footer stating "0 File(s), 0 Dir(s)".\r
645 //\r
646 if (!Sfo) {\r
647 PrintNonSfoHeader (RootPath);\r
648 if (ShellStatus != SHELL_ABORTED) {\r
649 PrintNonSfoFooter (FileCount, FileSize, DirCount);\r
650 }\r
651 }\r
652 } else {\r
653 return (SHELL_NOT_FOUND);\r
654 }\r
929fb3be
JC
655 }\r
656\r
657 if (Found != NULL) {\r
658 *Found = FoundOne;\r
659 }\r
660\r
a405b86d 661 return (ShellStatus);\r
662}\r
663\r
664STATIC CONST SHELL_PARAM_ITEM LsParamList[] = {\r
665 {L"-r", TypeFlag},\r
666 {L"-a", TypeStart},\r
667 {L"-sfo", TypeFlag},\r
668 {NULL, TypeMax}\r
669 };\r
670\r
671/**\r
672 Function for 'ls' command.\r
673\r
674 @param[in] ImageHandle Handle to the Image (NULL if Internal).\r
675 @param[in] SystemTable Pointer to the System Table (NULL if Internal).\r
676**/\r
677SHELL_STATUS\r
678EFIAPI\r
679ShellCommandRunLs (\r
680 IN EFI_HANDLE ImageHandle,\r
681 IN EFI_SYSTEM_TABLE *SystemTable\r
682 )\r
683{\r
684 EFI_STATUS Status;\r
685 LIST_ENTRY *Package;\r
686 CHAR16 *ProblemParam;\r
687 CONST CHAR16 *Attribs;\r
688 SHELL_STATUS ShellStatus;\r
689 UINT64 RequiredAttributes;\r
690 CONST CHAR16 *PathName;\r
691 CONST CHAR16 *CurDir;\r
692 UINTN Count;\r
693 CHAR16 *FullPath;\r
694 UINTN Size;\r
b54fd049 695 EFI_TIME TheTime;\r
929fb3be 696 CHAR16 *SearchString;\r
8d3f4281 697 BOOLEAN ListUnfiltered;\r
a405b86d 698\r
699 Size = 0;\r
700 FullPath = NULL;\r
701 ProblemParam = NULL;\r
702 Attribs = NULL;\r
703 ShellStatus = SHELL_SUCCESS;\r
704 RequiredAttributes = 0;\r
705 PathName = NULL;\r
929fb3be 706 SearchString = NULL;\r
a405b86d 707 CurDir = NULL;\r
708 Count = 0;\r
8d3f4281 709 ListUnfiltered = FALSE;\r
a405b86d 710\r
711 //\r
712 // initialize the shell lib (we must be in non-auto-init...)\r
713 //\r
714 Status = ShellInitialize();\r
715 ASSERT_EFI_ERROR(Status);\r
716\r
717 //\r
718 // Fix local copies of the protocol pointers\r
719 //\r
720 Status = CommandInit();\r
721 ASSERT_EFI_ERROR(Status);\r
722\r
723 //\r
724 // parse the command line\r
725 //\r
726 Status = ShellCommandLineParse (LsParamList, &Package, &ProblemParam, TRUE);\r
727 if (EFI_ERROR(Status)) {\r
728 if (Status == EFI_VOLUME_CORRUPTED && ProblemParam != NULL) {\r
ba0014b9 729 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), gShellLevel2HiiHandle, L"ls", ProblemParam);\r
a405b86d 730 FreePool(ProblemParam);\r
731 ShellStatus = SHELL_INVALID_PARAMETER;\r
732 } else {\r
733 ASSERT(FALSE);\r
734 }\r
735 } else {\r
736 //\r
737 // check for "-?"\r
738 //\r
739 if (ShellCommandLineGetFlag(Package, L"-?")) {\r
740 ASSERT(FALSE);\r
741 }\r
742\r
743 if (ShellCommandLineGetCount(Package) > 2) {\r
ba0014b9 744 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_MANY), gShellLevel2HiiHandle, L"ls");\r
a405b86d 745 ShellStatus = SHELL_INVALID_PARAMETER;\r
746 } else {\r
747 //\r
748 // check for -a\r
749 //\r
750 if (ShellCommandLineGetFlag(Package, L"-a")) {\r
751 for ( Attribs = ShellCommandLineGetValue(Package, L"-a")\r
752 ; Attribs != NULL && *Attribs != CHAR_NULL && ShellStatus == SHELL_SUCCESS\r
753 ; Attribs++\r
754 ){\r
755 switch (*Attribs) {\r
756 case L'a':\r
757 case L'A':\r
758 RequiredAttributes |= EFI_FILE_ARCHIVE;\r
759 Count++;\r
760 continue;\r
761 case L's':\r
762 case L'S':\r
763 RequiredAttributes |= EFI_FILE_SYSTEM;\r
764 Count++;\r
765 continue;\r
766 case L'h':\r
767 case L'H':\r
768 RequiredAttributes |= EFI_FILE_HIDDEN;\r
769 Count++;\r
770 continue;\r
771 case L'r':\r
772 case L'R':\r
773 RequiredAttributes |= EFI_FILE_READ_ONLY;\r
774 Count++;\r
775 continue;\r
776 case L'd':\r
777 case L'D':\r
778 RequiredAttributes |= EFI_FILE_DIRECTORY;\r
779 Count++;\r
780 continue;\r
781 default:\r
ba0014b9 782 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_ATTRIBUTE), gShellLevel2HiiHandle, L"ls", ShellCommandLineGetValue(Package, L"-a"));\r
a405b86d 783 ShellStatus = SHELL_INVALID_PARAMETER;\r
784 break;\r
785 } // switch\r
786 } // for loop\r
787 //\r
788 // if nothing is specified all are specified\r
789 //\r
790 if (RequiredAttributes == 0) {\r
791 RequiredAttributes = EFI_FILE_VALID_ATTR;\r
792 }\r
793 } // if -a present\r
794 if (ShellStatus == SHELL_SUCCESS) {\r
795 PathName = ShellCommandLineGetRawValue(Package, 1);\r
796 if (PathName == NULL) {\r
929fb3be
JC
797 //\r
798 // Nothing specified... must start from current directory\r
799 //\r
a405b86d 800 CurDir = gEfiShellProtocol->GetCurDir(NULL);\r
801 if (CurDir == NULL) {\r
802 ShellStatus = SHELL_NOT_FOUND;\r
ba0014b9 803 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_NO_CWD), gShellLevel2HiiHandle, L"ls");\r
a405b86d 804 }\r
8d3f4281 805 ListUnfiltered = TRUE;\r
929fb3be
JC
806 //\r
807 // Copy to the 2 strings for starting path and file search string\r
808 //\r
809 ASSERT(SearchString == NULL);\r
810 ASSERT(FullPath == NULL);\r
811 StrnCatGrow(&SearchString, NULL, L"*", 0);\r
812 StrnCatGrow(&FullPath, NULL, CurDir, 0);\r
fbd2dfad
QS
813 Size = FullPath != NULL? StrSize(FullPath) : 0;\r
814 StrnCatGrow(&FullPath, &Size, L"\\", 0);\r
929fb3be 815 } else {\r
2ec013ce 816 if (StrStr(PathName, L":") == NULL && gEfiShellProtocol->GetCurDir(NULL) == NULL) {\r
929fb3be
JC
817 //\r
818 // If we got something and it doesnt have a fully qualified path, then we needed to have a CWD.\r
819 //\r
2ec013ce 820 ShellStatus = SHELL_NOT_FOUND;\r
ba0014b9 821 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_NO_CWD), gShellLevel2HiiHandle, L"ls");\r
2ec013ce 822 } else {\r
929fb3be
JC
823 //\r
824 // We got a valid fully qualified path or we have a CWD\r
825 //\r
2ec013ce 826 ASSERT((FullPath == NULL && Size == 0) || (FullPath != NULL));\r
929fb3be
JC
827 if (StrStr(PathName, L":") == NULL) {\r
828 StrnCatGrow(&FullPath, &Size, gEfiShellProtocol->GetCurDir(NULL), 0);\r
3a8406ad
SQ
829 if (FullPath == NULL) {\r
830 ShellCommandLineFreeVarList (Package);\r
831 return SHELL_OUT_OF_RESOURCES;\r
832 }\r
fbd2dfad
QS
833 Size = FullPath != NULL? StrSize(FullPath) : 0;\r
834 StrnCatGrow(&FullPath, &Size, L"\\", 0);\r
929fb3be 835 }\r
2ec013ce 836 StrnCatGrow(&FullPath, &Size, PathName, 0);\r
3a8406ad
SQ
837 if (FullPath == NULL) {\r
838 ShellCommandLineFreeVarList (Package);\r
839 return SHELL_OUT_OF_RESOURCES;\r
840 }\r
ba0014b9 841\r
2ec013ce 842 if (ShellIsDirectory(PathName) == EFI_SUCCESS) {\r
929fb3be
JC
843 //\r
844 // is listing ends with a directory, then we list all files in that directory\r
845 //\r
8d3f4281 846 ListUnfiltered = TRUE;\r
929fb3be
JC
847 StrnCatGrow(&SearchString, NULL, L"*", 0);\r
848 } else {\r
849 //\r
850 // must split off the search part that applies to files from the end of the directory part\r
851 //\r
47ec9356 852 StrnCatGrow(&SearchString, NULL, FullPath, 0);\r
795c78cf
RN
853 if (SearchString == NULL) {\r
854 FreePool (FullPath);\r
855 ShellCommandLineFreeVarList (Package);\r
856 return SHELL_OUT_OF_RESOURCES;\r
857 }\r
47ec9356
RN
858 PathRemoveLastItem (FullPath);\r
859 CopyMem (SearchString, SearchString + StrLen (FullPath), StrSize (SearchString + StrLen (FullPath)));\r
2ec013ce 860 }\r
a405b86d 861 }\r
a405b86d 862 }\r
b54fd049 863 Status = gRT->GetTime(&TheTime, NULL);\r
2e8e9ed5 864 if (EFI_ERROR(Status)) {\r
ba0014b9 865 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_UEFI_FUNC_WARN), gShellLevel2HiiHandle, L"ls", L"gRT->GetTime", Status);\r
2e8e9ed5 866 TheTime.TimeZone = EFI_UNSPECIFIED_TIMEZONE;\r
867 }\r
868\r
a405b86d 869 if (ShellStatus == SHELL_SUCCESS) {\r
870 ShellStatus = PrintLsOutput(\r
871 ShellCommandLineGetFlag(Package, L"-r"),\r
872 RequiredAttributes,\r
929fb3be 873 ShellCommandLineGetFlag(Package, L"-sfo"),\r
a405b86d 874 FullPath,\r
929fb3be
JC
875 SearchString,\r
876 NULL,\r
a405b86d 877 Count,\r
8d3f4281
MH
878 TheTime.TimeZone,\r
879 ListUnfiltered\r
a405b86d 880 );\r
881 if (ShellStatus == SHELL_NOT_FOUND) {\r
ba0014b9 882 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_LS_FILE_NOT_FOUND), gShellLevel2HiiHandle, L"ls", FullPath);\r
a405b86d 883 } else if (ShellStatus == SHELL_INVALID_PARAMETER) {\r
ba0014b9 884 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellLevel2HiiHandle, L"ls", FullPath);\r
a7224eef 885 } else if (ShellStatus == SHELL_ABORTED) {\r
886 //\r
887 // Ignore aborting.\r
888 //\r
a405b86d 889 } else if (ShellStatus != SHELL_SUCCESS) {\r
ba0014b9 890 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellLevel2HiiHandle, L"ls", FullPath);\r
a405b86d 891 }\r
892 }\r
893 }\r
894 }\r
895 }\r
896\r
a405b86d 897 //\r
929fb3be 898 // Free memory allocated\r
a405b86d 899 //\r
929fb3be
JC
900 SHELL_FREE_NON_NULL(SearchString);\r
901 SHELL_FREE_NON_NULL(FullPath);\r
a405b86d 902 ShellCommandLineFreeVarList (Package);\r
903\r
904 return (ShellStatus);\r
905}\r